Files
filament/filament/backend/src/vulkan/VulkanHandles.cpp
T

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