284 lines
10 KiB
C++
284 lines
10 KiB
C++
/*
|
|
* Copyright (C) 2018 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "VulkanHandles.h"
|
|
|
|
#include "VulkanConstants.h"
|
|
#include "VulkanMemory.h"
|
|
#include "VulkanPlatform.h"
|
|
|
|
#include <utils/Panic.h>
|
|
|
|
using namespace bluevk;
|
|
|
|
namespace filament::backend {
|
|
|
|
static void flipVertically(VkRect2D* rect, uint32_t framebufferHeight) {
|
|
rect->offset.y = framebufferHeight - rect->offset.y - rect->extent.height;
|
|
}
|
|
|
|
static void flipVertically(VkViewport* rect, uint32_t framebufferHeight) {
|
|
rect->y = framebufferHeight - rect->y - rect->height;
|
|
}
|
|
|
|
static void clampToFramebuffer(VkRect2D* rect, uint32_t fbWidth, uint32_t fbHeight) {
|
|
int32_t x = std::max(rect->offset.x, 0);
|
|
int32_t y = std::max(rect->offset.y, 0);
|
|
int32_t right = std::min(rect->offset.x + (int32_t) rect->extent.width, (int32_t) fbWidth);
|
|
int32_t top = std::min(rect->offset.y + (int32_t) rect->extent.height, (int32_t) fbHeight);
|
|
rect->offset.x = std::min(x, (int32_t) fbWidth);
|
|
rect->offset.y = std::min(y, (int32_t) fbHeight);
|
|
rect->extent.width = std::max(right - x, 0);
|
|
rect->extent.height = std::max(top - y, 0);
|
|
}
|
|
|
|
VulkanProgram::VulkanProgram(VulkanContext& context, const Program& builder) noexcept :
|
|
HwProgram(builder.getName()), context(context) {
|
|
auto const& blobs = builder.getShadersSource();
|
|
VkShaderModule* modules[2] = { &bundle.vertex, &bundle.fragment };
|
|
bool missing = false;
|
|
for (size_t i = 0; i < Program::SHADER_TYPE_COUNT; i++) {
|
|
const auto& blob = blobs[i];
|
|
VkShaderModule* module = modules[i];
|
|
if (blob.empty()) {
|
|
missing = true;
|
|
continue;
|
|
}
|
|
VkShaderModuleCreateInfo moduleInfo = {};
|
|
moduleInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
|
moduleInfo.codeSize = blob.size();
|
|
moduleInfo.pCode = (uint32_t*) blob.data();
|
|
VkResult result = vkCreateShaderModule(context.device, &moduleInfo, VKALLOC, module);
|
|
ASSERT_POSTCONDITION(result == VK_SUCCESS, "Unable to create shader module.");
|
|
}
|
|
|
|
// Output a warning because it's okay to encounter empty blobs, but it's not okay to use
|
|
// this program handle in a draw call.
|
|
if (missing) {
|
|
utils::slog.w << "Missing SPIR-V shader: " << builder.getName().c_str() << utils::io::endl;
|
|
return;
|
|
}
|
|
|
|
// Make a copy of the binding map
|
|
samplerGroupInfo = builder.getSamplerGroupInfo();
|
|
if constexpr (FILAMENT_VULKAN_VERBOSE) {
|
|
utils::slog.d << "Created VulkanProgram " << builder
|
|
<< ", shaders = (" << bundle.vertex << ", " << bundle.fragment << ")"
|
|
<< utils::io::endl;
|
|
}
|
|
}
|
|
|
|
VulkanProgram::VulkanProgram(VulkanContext& context, VkShaderModule vs, VkShaderModule fs) noexcept :
|
|
context(context) {
|
|
bundle.vertex = vs;
|
|
bundle.fragment = fs;
|
|
}
|
|
|
|
VulkanProgram::~VulkanProgram() {
|
|
vkDestroyShaderModule(context.device, bundle.vertex, VKALLOC);
|
|
vkDestroyShaderModule(context.device, bundle.fragment, VKALLOC);
|
|
}
|
|
|
|
// Creates a special "default" render target (i.e. associated with the swap chain)
|
|
VulkanRenderTarget::VulkanRenderTarget() : HwRenderTarget(0, 0), mOffscreen(false), mSamples(1) {}
|
|
|
|
void VulkanRenderTarget::bindToSwapChain(VulkanSwapChain& swapChain) {
|
|
assert_invariant(!mOffscreen);
|
|
mColor[0] = { .texture = &swapChain.getColorTexture() };
|
|
mDepth = { .texture = &swapChain.getDepthTexture() };
|
|
width = swapChain.clientSize.width;
|
|
height = swapChain.clientSize.height;
|
|
}
|
|
|
|
VulkanRenderTarget::VulkanRenderTarget(VulkanContext& context, uint32_t width, uint32_t height,
|
|
uint8_t samples, VulkanAttachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT],
|
|
VulkanAttachment depthStencil[2], VulkanStagePool& stagePool) :
|
|
HwRenderTarget(width, height), mOffscreen(true), mSamples(samples) {
|
|
for (int index = 0; index < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; index++) {
|
|
mColor[index] = color[index];
|
|
}
|
|
mDepth = depthStencil[0];
|
|
VulkanTexture* depthTexture = mDepth.texture;
|
|
|
|
if (samples == 1) {
|
|
return;
|
|
}
|
|
|
|
// Constrain the sample count according to both kinds of sample count masks obtained from
|
|
// VkPhysicalDeviceProperties. This is consistent with the VulkanTexture constructor.
|
|
const auto& limits = context.physicalDeviceProperties.limits;
|
|
mSamples = samples = reduceSampleCount(samples, limits.framebufferDepthSampleCounts &
|
|
limits.framebufferColorSampleCounts);
|
|
|
|
// Create sidecar MSAA textures for color attachments if they don't already exist.
|
|
for (int index = 0; index < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; index++) {
|
|
const VulkanAttachment& spec = color[index];
|
|
VulkanTexture* texture = spec.texture;
|
|
if (texture && texture->samples == 1) {
|
|
VulkanTexture* msTexture = texture->getSidecar();
|
|
if (UTILS_UNLIKELY(msTexture == nullptr)) {
|
|
msTexture = new VulkanTexture(context, texture->target, texture->levels,
|
|
texture->format, samples, texture->width, texture->height, texture->depth,
|
|
texture->usage, stagePool);
|
|
texture->setSidecar(msTexture);
|
|
}
|
|
mMsaaAttachments[index] = { .texture = msTexture };
|
|
}
|
|
if (texture && texture->samples > 1) {
|
|
mMsaaAttachments[index] = mColor[index];
|
|
}
|
|
}
|
|
|
|
if (depthTexture == nullptr) {
|
|
return;
|
|
}
|
|
|
|
// There is no need for sidecar depth if the depth texture is already MSAA.
|
|
if (depthTexture->samples > 1) {
|
|
mMsaaDepthAttachment = mDepth;
|
|
return;
|
|
}
|
|
|
|
// Create sidecar MSAA texture for the depth attachment if it does not already exist.
|
|
VulkanTexture* msTexture = depthTexture->getSidecar();
|
|
if (UTILS_UNLIKELY(msTexture == nullptr)) {
|
|
msTexture = new VulkanTexture(context, depthTexture->target, depthTexture->levels,
|
|
depthTexture->format, samples, depthTexture->width, depthTexture->height,
|
|
depthTexture->depth, depthTexture->usage, stagePool);
|
|
depthTexture->setSidecar(msTexture);
|
|
}
|
|
|
|
mMsaaDepthAttachment = {
|
|
.texture = msTexture,
|
|
.level = mDepth.level,
|
|
.layer = mDepth.layer,
|
|
};
|
|
}
|
|
|
|
void VulkanRenderTarget::transformClientRectToPlatform(VkRect2D* bounds) const {
|
|
const auto& extent = getExtent();
|
|
flipVertically(bounds, extent.height);
|
|
clampToFramebuffer(bounds, extent.width, extent.height);
|
|
}
|
|
|
|
void VulkanRenderTarget::transformClientRectToPlatform(VkViewport* bounds) const {
|
|
flipVertically(bounds, getExtent().height);
|
|
}
|
|
|
|
VkExtent2D VulkanRenderTarget::getExtent() const {
|
|
return {width, height};
|
|
}
|
|
|
|
VulkanAttachment VulkanRenderTarget::getColor(int target) const {
|
|
return mColor[target];
|
|
}
|
|
|
|
VulkanAttachment VulkanRenderTarget::getMsaaColor(int target) const {
|
|
return mMsaaAttachments[target];
|
|
}
|
|
|
|
VulkanAttachment VulkanRenderTarget::getDepth() const {
|
|
return mDepth;
|
|
}
|
|
|
|
VulkanAttachment VulkanRenderTarget::getMsaaDepth() const {
|
|
return mMsaaDepthAttachment;
|
|
}
|
|
|
|
uint8_t VulkanRenderTarget::getColorTargetCount(const VulkanRenderPass& pass) const {
|
|
if (!mOffscreen) {
|
|
return 1;
|
|
}
|
|
uint8_t count = 0;
|
|
for (uint8_t i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; i++) {
|
|
if (!mColor[i].texture) {
|
|
continue;
|
|
}
|
|
// NOTE: This must be consistent with VkRenderPass construction (see VulkanFboCache).
|
|
if (!(pass.params.subpassMask & (1 << i)) || pass.currentSubpass == 1) {
|
|
count++;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
VulkanVertexBuffer::VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& stagePool,
|
|
uint8_t bufferCount, uint8_t attributeCount,
|
|
uint32_t elementCount, AttributeArray const& attribs) :
|
|
HwVertexBuffer(bufferCount, attributeCount, elementCount, attribs),
|
|
buffers(bufferCount, nullptr) {}
|
|
|
|
|
|
VulkanBufferObject::VulkanBufferObject(VulkanContext& context, VulkanStagePool& stagePool,
|
|
uint32_t byteCount, BufferObjectBinding bindingType, BufferUsage usage)
|
|
: HwBufferObject(byteCount),
|
|
buffer(context, stagePool, getBufferObjectUsage(bindingType), byteCount),
|
|
bindingType(bindingType) {
|
|
}
|
|
|
|
void VulkanRenderPrimitive::setPrimitiveType(PrimitiveType pt) {
|
|
this->type = pt;
|
|
switch (pt) {
|
|
case PrimitiveType::NONE:
|
|
case PrimitiveType::POINTS:
|
|
primitiveTopology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
|
|
break;
|
|
case PrimitiveType::LINES:
|
|
primitiveTopology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
|
|
break;
|
|
case PrimitiveType::LINE_STRIP:
|
|
primitiveTopology = VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
|
|
break;
|
|
case PrimitiveType::TRIANGLES:
|
|
primitiveTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
|
|
break;
|
|
case PrimitiveType::TRIANGLE_STRIP:
|
|
primitiveTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void VulkanRenderPrimitive::setBuffers(VulkanVertexBuffer* vertexBuffer,
|
|
VulkanIndexBuffer* indexBuffer) {
|
|
this->vertexBuffer = vertexBuffer;
|
|
this->indexBuffer = indexBuffer;
|
|
}
|
|
|
|
VulkanTimerQuery::VulkanTimerQuery(VulkanContext& context) : mContext(context) {
|
|
std::unique_lock<utils::Mutex> lock(context.timestamps.mutex);
|
|
utils::bitset32& bitset = context.timestamps.used;
|
|
const size_t maxTimers = bitset.size();
|
|
assert_invariant(bitset.count() < maxTimers);
|
|
for (size_t timerIndex = 0; timerIndex < maxTimers; ++timerIndex) {
|
|
if (!bitset.test(timerIndex)) {
|
|
bitset.set(timerIndex);
|
|
startingQueryIndex = timerIndex * 2;
|
|
stoppingQueryIndex = timerIndex * 2 + 1;
|
|
return;
|
|
}
|
|
}
|
|
utils::slog.e << "More than " << maxTimers << " timers are not supported." << utils::io::endl;
|
|
startingQueryIndex = 0;
|
|
stoppingQueryIndex = 1;
|
|
}
|
|
|
|
VulkanTimerQuery::~VulkanTimerQuery() {
|
|
std::unique_lock<utils::Mutex> lock(mContext.timestamps.mutex);
|
|
mContext.timestamps.used.unset(startingQueryIndex / 2);
|
|
}
|
|
|
|
} // namespace filament::backend
|