Vulkan: add support for MRT.
This commit is contained in:
@@ -53,7 +53,11 @@ public:
|
||||
};
|
||||
|
||||
class MRT {
|
||||
TargetBufferInfo mInfos[4];
|
||||
public:
|
||||
static constexpr int TARGET_COUNT = 4;
|
||||
|
||||
private:
|
||||
TargetBufferInfo mInfos[TARGET_COUNT];
|
||||
|
||||
public:
|
||||
TargetBufferInfo operator[](size_t i) const noexcept {
|
||||
|
||||
@@ -164,7 +164,7 @@ public:
|
||||
};
|
||||
|
||||
MetalRenderTarget(MetalContext* context, uint32_t width, uint32_t height, uint8_t samples,
|
||||
Attachment colorAttachments[4], Attachment depthAttachment);
|
||||
Attachment colorAttachments[MRT::TARGET_COUNT], Attachment depthAttachment);
|
||||
explicit MetalRenderTarget(MetalContext* context)
|
||||
: HwRenderTarget(0, 0), context(context), defaultRenderTarget(true) {}
|
||||
|
||||
@@ -187,11 +187,11 @@ private:
|
||||
bool defaultRenderTarget = false;
|
||||
uint8_t samples = 1;
|
||||
|
||||
Attachment color[4] = {};
|
||||
Attachment color[MRT::TARGET_COUNT] = {};
|
||||
Attachment depth = {};
|
||||
|
||||
// "Sidecar" textures used to implement automatic MSAA resolve.
|
||||
id<MTLTexture> multisampledColor[4] = { 0 };
|
||||
id<MTLTexture> multisampledColor[MRT::TARGET_COUNT] = { 0 };
|
||||
id<MTLTexture> multisampledDepth = nil;
|
||||
};
|
||||
|
||||
|
||||
@@ -476,13 +476,13 @@ void MetalTexture::updateLodRange(uint32_t level) {
|
||||
}
|
||||
|
||||
MetalRenderTarget::MetalRenderTarget(MetalContext* context, uint32_t width, uint32_t height,
|
||||
uint8_t samples, Attachment colorAttachments[4], Attachment depthAttachment) :
|
||||
uint8_t samples, Attachment colorAttachments[MRT::TARGET_COUNT], Attachment depthAttachment) :
|
||||
HwRenderTarget(width, height), context(context), samples(samples) {
|
||||
// If we were given a single-sampled texture but the samples parameter is > 1, we create
|
||||
// multisampled sidecar textures and do a resolve automatically.
|
||||
const bool msaaResolve = samples > 1;
|
||||
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
for (size_t i = 0; i < MRT::TARGET_COUNT; i++) {
|
||||
if (!colorAttachments[i]) {
|
||||
continue;
|
||||
}
|
||||
@@ -523,7 +523,7 @@ void MetalRenderTarget::setUpRenderPassAttachments(MTLRenderPassDescriptor* desc
|
||||
|
||||
const auto discardFlags = params.flags.discardEnd;
|
||||
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
for (size_t i = 0; i < MRT::TARGET_COUNT; i++) {
|
||||
Attachment attachment = getColorAttachment(i);
|
||||
if (!attachment) {
|
||||
continue;
|
||||
@@ -566,7 +566,7 @@ void MetalRenderTarget::setUpRenderPassAttachments(MTLRenderPassDescriptor* desc
|
||||
}
|
||||
|
||||
MetalRenderTarget::Attachment MetalRenderTarget::getColorAttachment(size_t index) {
|
||||
assert(index < 4);
|
||||
assert(index < MRT::TARGET_COUNT);
|
||||
Attachment result = color[index];
|
||||
if (index == 0 && defaultRenderTarget) {
|
||||
result.texture = acquireDrawable(context);
|
||||
|
||||
@@ -46,7 +46,7 @@ VulkanBinder::VulkanBinder() : mDefaultRasterState(createDefaultRasterState()) {
|
||||
mColorBlendState = VkPipelineColorBlendStateCreateInfo{};
|
||||
mColorBlendState.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
|
||||
mColorBlendState.attachmentCount = 1;
|
||||
mColorBlendState.pAttachments = &mPipelineKey.rasterState.blending;
|
||||
mColorBlendState.pAttachments = mColorBlendAttachments;
|
||||
mShaderStages[0] = VkPipelineShaderStageCreateInfo{};
|
||||
mShaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
mShaderStages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
@@ -260,8 +260,17 @@ bool VulkanBinder::getOrCreatePipeline(VkPipeline* pipeline) noexcept {
|
||||
pipelineCreateInfo.pDepthStencilState = &mPipelineKey.rasterState.depthStencil;
|
||||
pipelineCreateInfo.pDynamicState = &dynamicState;
|
||||
|
||||
// Filament assumes consistent blend state across all color attachments.
|
||||
mColorBlendState.attachmentCount = mPipelineKey.rasterState.getColorTargetCount;
|
||||
for (auto& target : mColorBlendAttachments) {
|
||||
target = mPipelineKey.rasterState.blending;
|
||||
}
|
||||
|
||||
// There are no color attachments if there is no bound fragment shader. (e.g. shadow map gen)
|
||||
mColorBlendState.attachmentCount = hasFragmentShader ? 1 : 0;
|
||||
// TODO: This should be handled in a higher layer.
|
||||
if (!hasFragmentShader) {
|
||||
mColorBlendState.attachmentCount = 0;
|
||||
}
|
||||
|
||||
#if FILAMENT_VULKAN_VERBOSE
|
||||
utils::slog.d << "vkCreateGraphicsPipelines with shaders = ("
|
||||
@@ -304,6 +313,7 @@ void VulkanBinder::bindRasterState(const RasterState& rasterState) noexcept {
|
||||
VkPipelineMultisampleStateCreateInfo& ms0 = mPipelineKey.rasterState.multisampling;
|
||||
const VkPipelineMultisampleStateCreateInfo& ms1 = rasterState.multisampling;
|
||||
if (
|
||||
mPipelineKey.rasterState.getColorTargetCount != rasterState.getColorTargetCount ||
|
||||
raster0.polygonMode != raster1.polygonMode ||
|
||||
raster0.cullMode != raster1.cullMode ||
|
||||
raster0.frontFace != raster1.frontFace ||
|
||||
@@ -659,6 +669,7 @@ static VulkanBinder::RasterState createDefaultRasterState() {
|
||||
blending,
|
||||
depthStencil,
|
||||
multisampling,
|
||||
1,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#define TNT_FILAMENT_DRIVER_VULKANBINDER_H
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/TargetBufferInfo.h>
|
||||
|
||||
#include <private/backend/Program.h>
|
||||
|
||||
@@ -101,6 +102,7 @@ public:
|
||||
VkPipelineColorBlendAttachmentState blending;
|
||||
VkPipelineDepthStencilStateCreateInfo depthStencil;
|
||||
VkPipelineMultisampleStateCreateInfo multisampling;
|
||||
uint32_t getColorTargetCount;
|
||||
};
|
||||
static_assert(std::is_pod<RasterState>::value, "RasterState must be a POD for fast hashing.");
|
||||
|
||||
@@ -225,6 +227,7 @@ private:
|
||||
VkDescriptorBufferInfo mDescriptorBuffers[UBUFFER_BINDING_COUNT];
|
||||
VkDescriptorImageInfo mDescriptorSamplers[SAMPLER_BINDING_COUNT];
|
||||
VkWriteDescriptorSet mDescriptorWrites[UBUFFER_BINDING_COUNT + SAMPLER_BINDING_COUNT];
|
||||
VkPipelineColorBlendAttachmentState mColorBlendAttachments[MRT::TARGET_COUNT];
|
||||
|
||||
// Current bindings are divided into two "keys" which are composed of a mix of actual values
|
||||
// (e.g., blending is OFF) and weak references to Vulkan objects (e.g., shader programs and
|
||||
|
||||
@@ -114,6 +114,8 @@ struct VulkanAttachment {
|
||||
VkDeviceMemory memory;
|
||||
VulkanTexture* texture = nullptr;
|
||||
VkImageLayout layout;
|
||||
uint8_t level;
|
||||
uint16_t layer;
|
||||
};
|
||||
|
||||
// The SwapContext is the set of objects that gets "swapped" at each beginFrame().
|
||||
|
||||
@@ -443,10 +443,28 @@ void VulkanDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, int) {
|
||||
void VulkanDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
|
||||
TargetBufferFlags targets, uint32_t width, uint32_t height, uint8_t samples,
|
||||
backend::MRT color, TargetBufferInfo depth, TargetBufferInfo stencil) {
|
||||
auto colorTexture = color[0].handle ? handle_cast<VulkanTexture>(mHandleMap, color[0].handle) : nullptr;
|
||||
auto depthTexture = depth.handle ? handle_cast<VulkanTexture>(mHandleMap, depth.handle) : nullptr;
|
||||
VulkanAttachment colorTargets[MRT::TARGET_COUNT] = {};
|
||||
for (int i = 0; i < MRT::TARGET_COUNT; i++) {
|
||||
if (color[i].handle) {
|
||||
colorTargets[i].texture = handle_cast<VulkanTexture>(mHandleMap, color[i].handle);
|
||||
}
|
||||
colorTargets[i].level = color[i].level;
|
||||
colorTargets[i].layer = color[i].layer;
|
||||
}
|
||||
|
||||
VulkanAttachment depthStencil[2] = {};
|
||||
TextureHandle handle = depth.handle;
|
||||
depthStencil[0].texture = handle ? handle_cast<VulkanTexture>(mHandleMap, handle) : nullptr;
|
||||
depthStencil[0].level = depth.level;
|
||||
depthStencil[0].layer = depth.layer;
|
||||
|
||||
handle = stencil.handle;
|
||||
depthStencil[1].texture = handle ? handle_cast<VulkanTexture>(mHandleMap, handle) : nullptr;
|
||||
depthStencil[1].level = stencil.level;
|
||||
depthStencil[1].layer = stencil.layer;
|
||||
|
||||
auto renderTarget = construct_handle<VulkanRenderTarget>(mHandleMap, rth, mContext,
|
||||
width, height, color[0], colorTexture, depth, depthTexture);
|
||||
width, height, colorTargets, depthStencil);
|
||||
mDisposer.createDisposable(renderTarget, [this, rth] () {
|
||||
destruct_handle<VulkanRenderTarget>(mHandleMap, rth);
|
||||
});
|
||||
@@ -859,50 +877,57 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
|
||||
const VkExtent2D extent = rt->getExtent();
|
||||
assert(extent.width > 0 && extent.height > 0);
|
||||
|
||||
const VulkanAttachment color = rt->getColor();
|
||||
const VulkanAttachment depth = rt->getDepth();
|
||||
const bool hasColor = color.format != VK_FORMAT_UNDEFINED;
|
||||
const bool hasDepth = depth.format != VK_FORMAT_UNDEFINED;
|
||||
|
||||
mDisposer.acquire(rt, mContext.currentCommands->resources);
|
||||
mDisposer.acquire(color.texture, mContext.currentCommands->resources);
|
||||
mDisposer.acquire(depth.texture, mContext.currentCommands->resources);
|
||||
|
||||
TargetBufferFlags discardStart = params.flags.discardStart;
|
||||
for (int i = 0; i < MRT::TARGET_COUNT; i++) {
|
||||
mDisposer.acquire(rt->getColor(i).texture, mContext.currentCommands->resources);
|
||||
}
|
||||
|
||||
// Filament has the expectation that the contents of the swap chain are not preserved on the
|
||||
// first render pass. Note however that its contents are often preserved on subsequent render
|
||||
// passes, due to multiple views.
|
||||
TargetBufferFlags discardStart = params.flags.discardStart;
|
||||
if (rt->invalidate()) {
|
||||
discardStart |= TargetBufferFlags::COLOR;
|
||||
}
|
||||
|
||||
VkRenderPass renderPass = mFramebufferCache.getRenderPass({
|
||||
.colorLayout = color.layout,
|
||||
// Create the VkRenderPass or fetch it from cache.
|
||||
VulkanFboCache::RenderPassKey rpkey = {
|
||||
.depthLayout = depth.layout,
|
||||
.colorFormat = color.format,
|
||||
.depthFormat = depth.format,
|
||||
.flags = {
|
||||
.clear = params.flags.clear,
|
||||
.discardStart = discardStart,
|
||||
.discardEnd = params.flags.discardEnd
|
||||
}
|
||||
});
|
||||
};
|
||||
for (int i = 0; i < MRT::TARGET_COUNT; i++) {
|
||||
rpkey.colorLayout[i] = rt->getColor(i).layout;
|
||||
rpkey.colorFormat[i] = rt->getColor(i).format;
|
||||
}
|
||||
|
||||
VkRenderPass renderPass = mFramebufferCache.getRenderPass(rpkey);
|
||||
mBinder.bindRenderPass(renderPass);
|
||||
|
||||
VulkanFboCache::FboKey fbo { .renderPass = renderPass };
|
||||
int numAttachments = 0;
|
||||
if (hasColor) {
|
||||
fbo.attachments[numAttachments++] = color.view;
|
||||
// Create the VkFramebuffer or fetch it from cache.
|
||||
VulkanFboCache::FboKey fbkey { .renderPass = renderPass };
|
||||
for (int i = 0, j = 0; i < MRT::TARGET_COUNT; i++) {
|
||||
if (rt->getColor(i).format != VK_FORMAT_UNDEFINED) {
|
||||
fbkey.color[j++] = rt->getColor(i).view;
|
||||
}
|
||||
}
|
||||
if (hasDepth) {
|
||||
fbo.attachments[numAttachments++] = depth.view;
|
||||
if (depth.format != VK_FORMAT_UNDEFINED) {
|
||||
fbkey.depth = depth.view;
|
||||
}
|
||||
VkFramebuffer vkfb = mFramebufferCache.getFramebuffer(fbkey, extent.width, extent.height, 1);
|
||||
|
||||
// Populate the structures required for vkCmdBeginRenderPass.
|
||||
VkRenderPassBeginInfo renderPassInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
||||
.renderPass = renderPass,
|
||||
.framebuffer = mFramebufferCache.getFramebuffer(fbo, extent.width, extent.height),
|
||||
.framebuffer = vkfb,
|
||||
.renderArea = {
|
||||
.offset = {params.viewport.left, params.viewport.bottom},
|
||||
.extent = {params.viewport.width, params.viewport.height}
|
||||
@@ -911,15 +936,21 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
|
||||
|
||||
rt->transformClientRectToPlatform(&renderPassInfo.renderArea);
|
||||
|
||||
VkClearValue clearValues[2] = {};
|
||||
if (hasColor) {
|
||||
VkClearValue& clearValue = clearValues[renderPassInfo.clearValueCount++];
|
||||
clearValue.color.float32[0] = params.clearColor.r;
|
||||
clearValue.color.float32[1] = params.clearColor.g;
|
||||
clearValue.color.float32[2] = params.clearColor.b;
|
||||
clearValue.color.float32[3] = params.clearColor.a;
|
||||
VkClearValue clearValues[MRT::TARGET_COUNT + 1] = {};
|
||||
|
||||
// NOTE: clearValues must be populated in the same order as the attachments array in
|
||||
// VulkanFboCache::getFramebuffer. Values must be provided regardless of whether Vulkan is
|
||||
// actually clearing that particular target.
|
||||
for (int i = 0; i < MRT::TARGET_COUNT; i++) {
|
||||
if (fbkey.color[i]) {
|
||||
VkClearValue& clearValue = clearValues[renderPassInfo.clearValueCount++];
|
||||
clearValue.color.float32[0] = params.clearColor.r;
|
||||
clearValue.color.float32[1] = params.clearColor.g;
|
||||
clearValue.color.float32[2] = params.clearColor.b;
|
||||
clearValue.color.float32[3] = params.clearColor.a;
|
||||
}
|
||||
}
|
||||
if (hasDepth) {
|
||||
if (fbkey.depth) {
|
||||
VkClearValue& clearValue = clearValues[renderPassInfo.clearValueCount++];
|
||||
clearValue.depthStencil = {(float) params.clearDepth, 0};
|
||||
}
|
||||
@@ -931,16 +962,16 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
|
||||
VK_SUBPASS_CONTENTS_INLINE);
|
||||
|
||||
VkViewport viewport = mContext.viewport = {
|
||||
.x = (float) params.viewport.left,
|
||||
.y = (float) params.viewport.bottom,
|
||||
.width = (float) params.viewport.width,
|
||||
.height = (float) params.viewport.height,
|
||||
.minDepth = 0.0f,
|
||||
.maxDepth = 1.0f
|
||||
.x = (float) params.viewport.left,
|
||||
.y = (float) params.viewport.bottom,
|
||||
.width = (float) params.viewport.width,
|
||||
.height = (float) params.viewport.height,
|
||||
.minDepth = 0.0f,
|
||||
.maxDepth = 1.0f
|
||||
};
|
||||
VkRect2D scissor {
|
||||
.offset = { std::max(0, (int32_t) viewport.x), std::max(0, (int32_t) viewport.y) },
|
||||
.extent = { (uint32_t) viewport.width, (uint32_t) viewport.height }
|
||||
.offset = { std::max(0, (int32_t) viewport.x), std::max(0, (int32_t) viewport.y) },
|
||||
.extent = { (uint32_t) viewport.width, (uint32_t) viewport.height }
|
||||
};
|
||||
|
||||
mCurrentRenderTarget->transformClientRectToPlatform(&scissor);
|
||||
@@ -1133,16 +1164,18 @@ void VulkanDriver::blit(TargetBufferFlags buffers,
|
||||
auto dstTarget = handle_cast<VulkanRenderTarget>(mHandleMap, dst);
|
||||
auto srcTarget = handle_cast<VulkanRenderTarget>(mHandleMap, src);
|
||||
|
||||
const int targetIndex = 0; // TODO: support MRT in blit
|
||||
|
||||
// In debug builds, verify that the two render targets have blittable formats.
|
||||
#ifndef NDEBUG
|
||||
const VkPhysicalDevice gpu = mContext.physicalDevice;
|
||||
VkFormatProperties info;
|
||||
vkGetPhysicalDeviceFormatProperties(gpu, srcTarget->getColor().format, &info);
|
||||
vkGetPhysicalDeviceFormatProperties(gpu, srcTarget->getColor(targetIndex).format, &info);
|
||||
if (!ASSERT_POSTCONDITION_NON_FATAL(info.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT,
|
||||
"Source format is not blittable")) {
|
||||
return;
|
||||
}
|
||||
vkGetPhysicalDeviceFormatProperties(gpu, dstTarget->getColor().format, &info);
|
||||
vkGetPhysicalDeviceFormatProperties(gpu, dstTarget->getColor(targetIndex).format, &info);
|
||||
if (!ASSERT_POSTCONDITION_NON_FATAL(info.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT,
|
||||
"Destination format is not blittable")) {
|
||||
return;
|
||||
@@ -1154,11 +1187,11 @@ void VulkanDriver::blit(TargetBufferFlags buffers,
|
||||
|
||||
const int32_t srcRight = srcRect.left + srcRect.width;
|
||||
const int32_t srcTop = srcRect.bottom + srcRect.height;
|
||||
const uint32_t srcLevel = srcTarget->getColorLevel();
|
||||
const uint32_t srcLevel = srcTarget->getColor(targetIndex).level;
|
||||
|
||||
const int32_t dstRight = dstRect.left + dstRect.width;
|
||||
const int32_t dstTop = dstRect.bottom + dstRect.height;
|
||||
const uint32_t dstLevel = dstTarget->getColorLevel();
|
||||
const uint32_t dstLevel = dstTarget->getColor(targetIndex).level;
|
||||
|
||||
const VkImageAspectFlags aspect = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
|
||||
@@ -1170,11 +1203,11 @@ void VulkanDriver::blit(TargetBufferFlags buffers,
|
||||
}};
|
||||
|
||||
auto vkblit = [=](VkCommandBuffer cmdbuffer) {
|
||||
VkImage srcImage = srcTarget->getColor().image;
|
||||
VkImage srcImage = srcTarget->getColor(targetIndex).image;
|
||||
VulkanTexture::transitionImageLayout(cmdbuffer, srcImage, VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, srcLevel, 1, 1, aspect);
|
||||
|
||||
VkImage dstImage = dstTarget->getColor().image;
|
||||
VkImage dstImage = dstTarget->getColor(targetIndex).image;
|
||||
VulkanTexture::transitionImageLayout(cmdbuffer, dstImage, VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, dstLevel, 1, 1, aspect);
|
||||
|
||||
@@ -1185,10 +1218,12 @@ void VulkanDriver::blit(TargetBufferFlags buffers,
|
||||
filter == SamplerMagFilter::NEAREST ? VK_FILTER_NEAREST : VK_FILTER_LINEAR);
|
||||
|
||||
VulkanTexture::transitionImageLayout(cmdbuffer, srcImage, VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
getTextureLayout(srcTarget->getColor().texture->usage), srcLevel, 1, 1, aspect);
|
||||
getTextureLayout(srcTarget->getColor(targetIndex).texture->usage), srcLevel, 1, 1,
|
||||
aspect);
|
||||
|
||||
VulkanTexture::transitionImageLayout(cmdbuffer, dstImage, VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
getTextureLayout(dstTarget->getColor().texture->usage), dstLevel, 1, 1, aspect);
|
||||
getTextureLayout(dstTarget->getColor(targetIndex).texture->usage), dstLevel, 1, 1,
|
||||
aspect);
|
||||
};
|
||||
|
||||
if (!mContext.currentCommands) {
|
||||
@@ -1241,15 +1276,17 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> r
|
||||
.colorWriteMask = (VkColorComponentFlags) (rasterState.colorWrite ? 0xf : 0x0),
|
||||
};
|
||||
|
||||
auto& vkraster = mContext.rasterState.rasterization;
|
||||
VkPipelineRasterizationStateCreateInfo& vkraster = mContext.rasterState.rasterization;
|
||||
vkraster.cullMode = getCullMode(rasterState.culling);
|
||||
vkraster.frontFace = getFrontFace(rasterState.inverseFrontFaces);
|
||||
vkraster.depthBiasEnable = (depthOffset.constant || depthOffset.slope) ? VK_TRUE : VK_FALSE;
|
||||
vkraster.depthBiasConstantFactor = depthOffset.constant;
|
||||
vkraster.depthBiasSlopeFactor = depthOffset.slope;
|
||||
|
||||
VulkanBinder::ProgramBundle shaderHandles = program->bundle;
|
||||
VulkanRenderTarget* rt = mCurrentRenderTarget;
|
||||
mContext.rasterState.getColorTargetCount = rt->getColorTargetCount();
|
||||
|
||||
VulkanBinder::ProgramBundle shaderHandles = program->bundle;
|
||||
|
||||
// Push state changes to the VulkanBinder instance. This is fast and does not make VK calls.
|
||||
mBinder.bindProgramBundle(shaderHandles);
|
||||
|
||||
@@ -23,21 +23,23 @@ namespace backend {
|
||||
|
||||
bool VulkanFboCache::RenderPassEq::operator()(const RenderPassKey& k1,
|
||||
const RenderPassKey& k2) const {
|
||||
return
|
||||
k1.colorLayout == k2.colorLayout &&
|
||||
k1.depthLayout == k2.depthLayout &&
|
||||
k1.colorFormat == k2.colorFormat &&
|
||||
k1.depthFormat == k2.depthFormat &&
|
||||
k1.flags.value == k2.flags.value;
|
||||
if (k1.flags.value != k2.flags.value) return false;
|
||||
if (k1.depthLayout != k2.depthLayout) return false;
|
||||
if (k1.depthFormat != k2.depthFormat) return false;
|
||||
for (int i = 0; i < MRT::TARGET_COUNT; i++) {
|
||||
if (k1.colorLayout[i] != k2.colorLayout[i]) return false;
|
||||
if (k1.colorFormat[i] != k2.colorFormat[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VulkanFboCache::FboKeyEqualFn::operator()(const FboKey& k1, const FboKey& k2) const {
|
||||
static_assert(sizeof(FboKey::attachments) == 3 * sizeof(VkImageView), "Unexpected count.");
|
||||
return
|
||||
k1.renderPass == k2.renderPass &&
|
||||
k1.attachments[0] == k2.attachments[0] &&
|
||||
k1.attachments[1] == k2.attachments[1] &&
|
||||
k1.attachments[2] == k2.attachments[2];
|
||||
if (k1.renderPass != k2.renderPass) return false;
|
||||
if (k1.depth != k2.depth) return false;
|
||||
for (int i = 0; i < MRT::TARGET_COUNT; i++) {
|
||||
if (k1.color[i] != k2.color[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
VulkanFboCache::VulkanFboCache(VulkanContext& context) : mContext(context) {}
|
||||
@@ -47,26 +49,31 @@ VulkanFboCache::~VulkanFboCache() {
|
||||
"Please explicitly call reset() while the VkDevice is still alive.");
|
||||
}
|
||||
|
||||
VkFramebuffer VulkanFboCache::getFramebuffer(FboKey config, uint32_t w, uint32_t h) noexcept {
|
||||
VkFramebuffer VulkanFboCache::getFramebuffer(FboKey config, uint32_t width,
|
||||
uint32_t height, uint32_t layers) noexcept {
|
||||
auto iter = mFramebufferCache.find(config);
|
||||
if (UTILS_LIKELY(iter != mFramebufferCache.end() && iter->second.handle != VK_NULL_HANDLE)) {
|
||||
iter.value().timestamp = mCurrentTime;
|
||||
return iter->second.handle;
|
||||
}
|
||||
VkImageView attachments[MRT::TARGET_COUNT + 1];
|
||||
uint32_t nAttachments = 0;
|
||||
for (auto attachment : config.attachments) {
|
||||
for (VkImageView attachment : config.color) {
|
||||
if (attachment) {
|
||||
nAttachments++;
|
||||
attachments[nAttachments++] = attachment;
|
||||
}
|
||||
}
|
||||
if (config.depth) {
|
||||
attachments[nAttachments++] = config.depth;
|
||||
}
|
||||
VkFramebufferCreateInfo info {
|
||||
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
|
||||
.renderPass = config.renderPass,
|
||||
.attachmentCount = nAttachments,
|
||||
.pAttachments = config.attachments,
|
||||
.width = w,
|
||||
.height = h,
|
||||
.layers = 1
|
||||
.pAttachments = attachments,
|
||||
.width = width,
|
||||
.height = height,
|
||||
.layers = layers,
|
||||
};
|
||||
mRenderPassRefCount[info.renderPass]++;
|
||||
VkFramebuffer framebuffer;
|
||||
@@ -82,22 +89,12 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey config) noexcept {
|
||||
iter.value().timestamp = mCurrentTime;
|
||||
return iter->second.handle;
|
||||
}
|
||||
const bool hasColor = config.colorFormat != VK_FORMAT_UNDEFINED;
|
||||
const bool hasDepth = config.depthFormat != VK_FORMAT_UNDEFINED;
|
||||
const bool isSwapChain = config.colorLayout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||
const bool isSwapChain = config.colorLayout[0] == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||
|
||||
// Set up some const aliases for terseness.
|
||||
const VkAttachmentLoadOp clear = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
const VkAttachmentLoadOp dontCare = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
const VkAttachmentLoadOp keep = VK_ATTACHMENT_LOAD_OP_LOAD;
|
||||
|
||||
const bool clearColor = any(config.flags.clear & TargetBufferFlags::COLOR);
|
||||
const bool discardColor = any(config.flags.discardStart & TargetBufferFlags::COLOR);
|
||||
const VkAttachmentLoadOp colorLoadOp = clearColor ? clear : (discardColor ? dontCare : keep);
|
||||
|
||||
const bool clearDepth = any(config.flags.clear & TargetBufferFlags::DEPTH);
|
||||
const bool discardDepth = any(config.flags.discardStart & TargetBufferFlags::DEPTH);
|
||||
const VkAttachmentLoadOp depthLoadOp = clearDepth ? clear : (discardDepth ? dontCare : keep);
|
||||
const VkAttachmentLoadOp kClear = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
const VkAttachmentLoadOp kDontCare = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
const VkAttachmentLoadOp kKeep = VK_ATTACHMENT_LOAD_OP_LOAD;
|
||||
|
||||
// In Vulkan, the subpass desc specifies the layout to transition to at the start of the render
|
||||
// pass, and the attachment description specifies the layout to transition to at the end.
|
||||
@@ -105,49 +102,34 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey config) noexcept {
|
||||
// swap chain. We keep our offscreen images in GENERAL layout, which is simple and prevents
|
||||
// thrashing the layout. Note that pipeline barriers are more powerful than render passes for
|
||||
// performing layout transitions, because they allow for per-miplevel transitions.
|
||||
struct { VkImageLayout subpass, initial, final; } colorLayouts;
|
||||
const bool discard = any(config.flags.discardStart & TargetBufferFlags::COLOR);
|
||||
struct { VkImageLayout subpass, initial, final; } colorLayouts[MRT::TARGET_COUNT];
|
||||
if (isSwapChain) {
|
||||
colorLayouts.subpass = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
colorLayouts.initial = discardColor ? VK_IMAGE_LAYOUT_UNDEFINED : colorLayouts.subpass;
|
||||
colorLayouts.final = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
colorLayouts[0].subpass = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
colorLayouts[0].initial = discard ? VK_IMAGE_LAYOUT_UNDEFINED : colorLayouts[0].subpass;
|
||||
colorLayouts[0].final = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
} else {
|
||||
colorLayouts.subpass = config.colorLayout;
|
||||
colorLayouts.initial = config.colorLayout;
|
||||
colorLayouts.final = config.colorLayout;
|
||||
for (int i = 0; i < MRT::TARGET_COUNT; i++) {
|
||||
colorLayouts[i].subpass = config.colorLayout[i];
|
||||
colorLayouts[i].initial = config.colorLayout[i];
|
||||
colorLayouts[i].final = config.colorLayout[i];
|
||||
}
|
||||
}
|
||||
|
||||
VkAttachmentReference colorAttachmentRef = {};
|
||||
VkAttachmentReference colorAttachmentRef[MRT::TARGET_COUNT] = {};
|
||||
VkAttachmentReference depthAttachmentRef = {};
|
||||
|
||||
const bool hasDepth = config.depthFormat != VK_FORMAT_UNDEFINED;
|
||||
|
||||
VkSubpassDescription subpass {
|
||||
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
.colorAttachmentCount = hasColor ? 1u : 0u,
|
||||
.pColorAttachments = hasColor ? &colorAttachmentRef : nullptr,
|
||||
.colorAttachmentCount = 0u,
|
||||
.pColorAttachments = colorAttachmentRef,
|
||||
.pDepthStencilAttachment = hasDepth ? &depthAttachmentRef : nullptr
|
||||
};
|
||||
|
||||
VkAttachmentDescription colorAttachment {
|
||||
.format = config.colorFormat,
|
||||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||||
.loadOp = colorLoadOp,
|
||||
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
|
||||
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
||||
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
||||
.initialLayout = colorLayouts.initial,
|
||||
.finalLayout = colorLayouts.final
|
||||
};
|
||||
VkAttachmentDescription depthAttachment {
|
||||
.format = config.depthFormat,
|
||||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||||
.loadOp = depthLoadOp,
|
||||
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
|
||||
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
||||
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
||||
.initialLayout = config.depthLayout,
|
||||
.finalLayout = config.depthLayout
|
||||
};
|
||||
VkAttachmentDescription attachments[MRT::TARGET_COUNT + 1] = {};
|
||||
|
||||
VkAttachmentDescription attachments[2];
|
||||
VkRenderPassCreateInfo renderPassInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
|
||||
.attachmentCount = 0u,
|
||||
@@ -157,17 +139,48 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey config) noexcept {
|
||||
.dependencyCount = 0u
|
||||
};
|
||||
|
||||
if (hasColor) {
|
||||
colorAttachmentRef.layout = colorLayouts.subpass;
|
||||
colorAttachmentRef.attachment = renderPassInfo.attachmentCount;
|
||||
attachments[renderPassInfo.attachmentCount++] = colorAttachment;
|
||||
int numAttachments = 0;
|
||||
for (int i = 0; i < MRT::TARGET_COUNT; i++) {
|
||||
if (config.colorFormat[i] == VK_FORMAT_UNDEFINED) {
|
||||
continue;
|
||||
}
|
||||
TargetBufferFlags flag = TargetBufferFlags(int(TargetBufferFlags::COLOR0) << i);
|
||||
bool clear = any(config.flags.clear & flag);
|
||||
bool discard = any(config.flags.discardStart & flag);
|
||||
colorAttachmentRef[numAttachments].layout = colorLayouts[i].subpass;
|
||||
colorAttachmentRef[numAttachments].attachment = numAttachments;
|
||||
attachments[numAttachments] = {
|
||||
.format = config.colorFormat[i],
|
||||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||||
.loadOp = clear ? kClear : (discard ? kDontCare : kKeep),
|
||||
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
|
||||
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
||||
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
||||
.initialLayout = colorLayouts[i].initial,
|
||||
.finalLayout = colorLayouts[i].final
|
||||
};
|
||||
++numAttachments;
|
||||
}
|
||||
subpass.colorAttachmentCount = numAttachments;
|
||||
|
||||
if (hasDepth) {
|
||||
bool clear = any(config.flags.clear & TargetBufferFlags::DEPTH);
|
||||
bool discard = any(config.flags.discardStart & TargetBufferFlags::DEPTH);
|
||||
depthAttachmentRef.layout = config.depthLayout;
|
||||
depthAttachmentRef.attachment = renderPassInfo.attachmentCount;
|
||||
attachments[renderPassInfo.attachmentCount++] = depthAttachment;
|
||||
depthAttachmentRef.attachment = numAttachments;
|
||||
attachments[numAttachments] = {
|
||||
.format = config.depthFormat,
|
||||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||||
.loadOp = clear ? kClear : (discard ? kDontCare : kKeep),
|
||||
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
|
||||
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
||||
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
||||
.initialLayout = config.depthLayout,
|
||||
.finalLayout = config.depthLayout
|
||||
};
|
||||
++numAttachments;
|
||||
}
|
||||
renderPassInfo.attachmentCount = numAttachments;
|
||||
|
||||
// Finally, create the VkRenderPass.
|
||||
VkRenderPass renderPass;
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
|
||||
#include <utils/Hash.h>
|
||||
|
||||
#include <backend/TargetBufferInfo.h>
|
||||
|
||||
#include <tsl/robin_map.h>
|
||||
|
||||
namespace filament {
|
||||
@@ -35,12 +37,11 @@ namespace backend {
|
||||
class VulkanFboCache {
|
||||
public:
|
||||
// RenderPassKey is a small POD representing the immutable state that is used to construct
|
||||
// a VkRenderPass. It is hashed and used as a lookup key. Portions of this are extracted
|
||||
// from backend::RenderPassParams.
|
||||
// a VkRenderPass. It is hashed and used as a lookup key.
|
||||
struct alignas(8) RenderPassKey {
|
||||
VkImageLayout colorLayout; // 4 bytes
|
||||
VkImageLayout colorLayout[MRT::TARGET_COUNT]; // 16 bytes
|
||||
VkFormat colorFormat[MRT::TARGET_COUNT]; // 16 bytes
|
||||
VkImageLayout depthLayout; // 4 bytes
|
||||
VkFormat colorFormat; // 4 bytes
|
||||
VkFormat depthFormat; // 4 bytes
|
||||
union {
|
||||
struct {
|
||||
@@ -57,20 +58,22 @@ public:
|
||||
VkRenderPass handle;
|
||||
uint32_t timestamp;
|
||||
};
|
||||
static_assert(sizeof(TargetBufferFlags) == 1, "TargetBufferFlags has unexpected size.");
|
||||
static_assert(sizeof(VkFormat) == 4, "VkFormat has unexpected size.");
|
||||
static_assert(sizeof(RenderPassKey) == 24, "RenderPassKey has unexpected size.");
|
||||
static_assert(sizeof(RenderPassKey) == 48, "RenderPassKey has unexpected size.");
|
||||
using RenderPassHash = utils::hash::MurmurHashFn<RenderPassKey>;
|
||||
struct RenderPassEq {
|
||||
bool operator()(const RenderPassKey& k1, const RenderPassKey& k2) const;
|
||||
};
|
||||
|
||||
// FboKey is a small POD representing the immutable state that we wish to configure
|
||||
// in VkFramebuffer. It is hashed and used as a lookup key. There are 1-3 attachments, but
|
||||
// in VkFramebuffer. It is hashed and used as a lookup key. There are several attachments, but
|
||||
// rather than storing a count, we simply zero out the unused slots. We do not bother storing
|
||||
// width and height in the key since they are immutable aspects of the image views.
|
||||
struct alignas(8) FboKey {
|
||||
VkRenderPass renderPass; // 8 bytes
|
||||
VkImageView attachments[3]; // 24 bytes
|
||||
VkImageView color[MRT::TARGET_COUNT]; // 32 bytes
|
||||
VkImageView depth; // 8 bytes
|
||||
};
|
||||
struct FboVal {
|
||||
VkFramebuffer handle;
|
||||
@@ -78,7 +81,7 @@ public:
|
||||
};
|
||||
static_assert(sizeof(VkRenderPass) == 8, "VkRenderPass has unexpected size.");
|
||||
static_assert(sizeof(VkImageView) == 8, "VkImageView has unexpected size.");
|
||||
static_assert(sizeof(FboKey) == 32, "FboKey has unexpected size.");
|
||||
static_assert(sizeof(FboKey) == 48, "FboKey has unexpected size.");
|
||||
using FboKeyHashFn = utils::hash::MurmurHashFn<FboKey>;
|
||||
struct FboKeyEqualFn {
|
||||
bool operator()(const FboKey& k1, const FboKey& k2) const;
|
||||
@@ -87,9 +90,12 @@ public:
|
||||
explicit VulkanFboCache(VulkanContext&);
|
||||
~VulkanFboCache();
|
||||
|
||||
// Retrieves or creates a VkFramebuffer handle. Width and height are used only when
|
||||
// creating the framebuffer (they are not used for lookup),
|
||||
VkFramebuffer getFramebuffer(FboKey config, uint32_t width, uint32_t height) noexcept;
|
||||
// Retrieves or creates a VkFramebuffer handle.
|
||||
//
|
||||
// NOTE: The dimensions are used only when creating the the framebuffer. They are not used for
|
||||
// lookup because the attachments in the FboKey already have dimensions.
|
||||
VkFramebuffer getFramebuffer(FboKey config, uint32_t width, uint32_t height,
|
||||
uint32_t layers) noexcept;
|
||||
|
||||
// Retrieves or creates a VkRenderPass handle.
|
||||
VkRenderPass getRenderPass(RenderPassKey config) noexcept;
|
||||
|
||||
@@ -102,58 +102,68 @@ static VulkanAttachment createOffscreenAttachment(VulkanTexture* tex) {
|
||||
// Creates a special "default" render target (i.e. associated with the swap chain)
|
||||
// Note that the attachment structs are unused in this case in favor of VulkanSurfaceContext.
|
||||
VulkanRenderTarget::VulkanRenderTarget(VulkanContext& context) : HwRenderTarget(0, 0),
|
||||
mContext(context), mOffscreen(false), mColorLevel(0), mDepthLevel(0) {}
|
||||
mContext(context), mOffscreen(false) {}
|
||||
|
||||
VulkanRenderTarget::VulkanRenderTarget(VulkanContext& context, uint32_t width, uint32_t height,
|
||||
TargetBufferInfo colorInfo, VulkanTexture* color, TargetBufferInfo depthInfo,
|
||||
VulkanTexture* depth) : HwRenderTarget(width, height), mContext(context), mOffscreen(true),
|
||||
mColorLevel(colorInfo.level), mDepthLevel(depthInfo.level) {
|
||||
mColor = color ? createOffscreenAttachment(color) : VulkanAttachment {};
|
||||
mDepth = depth ? createOffscreenAttachment(depth) : VulkanAttachment {};
|
||||
VulkanAttachment color[MRT::TARGET_COUNT], VulkanAttachment depthStencil[2]) :
|
||||
HwRenderTarget(width, height), mContext(context), mOffscreen(true) {
|
||||
|
||||
for (int targetIndex = 0; targetIndex < MRT::TARGET_COUNT; targetIndex++) {
|
||||
VulkanAttachment& attachment = mColor[targetIndex];
|
||||
if (!color[targetIndex].texture) {
|
||||
attachment = {};
|
||||
continue;
|
||||
}
|
||||
|
||||
attachment = createOffscreenAttachment(color[targetIndex].texture);
|
||||
attachment.level = color[targetIndex].level;
|
||||
|
||||
// We cannot use the VkImageView that's in the texture because we need to select a single level.
|
||||
if (color) {
|
||||
VkImageViewCreateInfo viewInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
.image = mColor.image,
|
||||
.format = mColor.format,
|
||||
.image = mColor[targetIndex].image,
|
||||
.format = mColor[targetIndex].format,
|
||||
.subresourceRange = {
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.baseMipLevel = colorInfo.level,
|
||||
.baseMipLevel = mColor[targetIndex].level,
|
||||
.levelCount = 1
|
||||
}
|
||||
};
|
||||
if (color->target == SamplerType::SAMPLER_CUBEMAP) {
|
||||
if (attachment.texture->target == SamplerType::SAMPLER_CUBEMAP) {
|
||||
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_CUBE;
|
||||
viewInfo.subresourceRange.layerCount = 6;
|
||||
} else if (color->target == SamplerType::SAMPLER_2D_ARRAY) {
|
||||
} else if (attachment.texture->target == SamplerType::SAMPLER_2D_ARRAY) {
|
||||
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
|
||||
viewInfo.subresourceRange.layerCount = 1;
|
||||
viewInfo.subresourceRange.baseArrayLayer = colorInfo.layer;
|
||||
viewInfo.subresourceRange.baseArrayLayer = attachment.layer;
|
||||
} else {
|
||||
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
viewInfo.subresourceRange.layerCount = 1;
|
||||
}
|
||||
vkCreateImageView(context.device, &viewInfo, VKALLOC, &mColor.view);
|
||||
vkCreateImageView(context.device, &viewInfo, VKALLOC, &attachment.view);
|
||||
}
|
||||
if (depth) {
|
||||
|
||||
mDepth = depthStencil[0].texture ? createOffscreenAttachment(depthStencil[0].texture) : VulkanAttachment {};
|
||||
mDepth.level = depthStencil[0].level;
|
||||
|
||||
VulkanTexture* depthTexture = mDepth.texture;
|
||||
if (depthTexture) {
|
||||
VkImageViewCreateInfo viewInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
.image = mDepth.image,
|
||||
.format = mDepth.format,
|
||||
.subresourceRange = {
|
||||
.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT,
|
||||
.baseMipLevel = depthInfo.level,
|
||||
.baseMipLevel = mDepth.level,
|
||||
.levelCount = 1
|
||||
}
|
||||
};
|
||||
if (depth->target == SamplerType::SAMPLER_CUBEMAP) {
|
||||
if (depthTexture->target == SamplerType::SAMPLER_CUBEMAP) {
|
||||
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_CUBE;
|
||||
viewInfo.subresourceRange.layerCount = 6;
|
||||
} else if (depth->target == SamplerType::SAMPLER_2D_ARRAY) {
|
||||
} else if (depthTexture->target == SamplerType::SAMPLER_2D_ARRAY) {
|
||||
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
|
||||
viewInfo.subresourceRange.layerCount = 1;
|
||||
viewInfo.subresourceRange.baseArrayLayer = depthInfo.layer;
|
||||
viewInfo.subresourceRange.baseArrayLayer = mDepth.layer;
|
||||
} else {
|
||||
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
viewInfo.subresourceRange.layerCount = 1;
|
||||
@@ -163,8 +173,10 @@ VulkanRenderTarget::VulkanRenderTarget(VulkanContext& context, uint32_t width, u
|
||||
}
|
||||
|
||||
VulkanRenderTarget::~VulkanRenderTarget() {
|
||||
if (mColor.view) {
|
||||
vkDestroyImageView(mContext.device, mColor.view, VKALLOC);
|
||||
for (int targetIndex = 0; targetIndex < MRT::TARGET_COUNT; targetIndex++) {
|
||||
if (mColor[targetIndex].view) {
|
||||
vkDestroyImageView(mContext.device, mColor[targetIndex].view, VKALLOC);
|
||||
}
|
||||
}
|
||||
if (mDepth.view) {
|
||||
vkDestroyImageView(mContext.device, mDepth.view, VKALLOC);
|
||||
@@ -221,14 +233,27 @@ VkExtent2D VulkanRenderTarget::getExtent() const {
|
||||
return mContext.currentSurface->surfaceCapabilities.currentExtent;
|
||||
}
|
||||
|
||||
VulkanAttachment VulkanRenderTarget::getColor() const {
|
||||
return mOffscreen ? mColor : getSwapContext(mContext).attachment;
|
||||
VulkanAttachment VulkanRenderTarget::getColor(int target) const {
|
||||
return (mOffscreen || target > 0) ? mColor[target] : getSwapContext(mContext).attachment;
|
||||
}
|
||||
|
||||
VulkanAttachment VulkanRenderTarget::getDepth() const {
|
||||
return mOffscreen ? mDepth : mContext.currentSurface->depth;
|
||||
}
|
||||
|
||||
int VulkanRenderTarget::getColorTargetCount() const {
|
||||
if (!mOffscreen) {
|
||||
return 1;
|
||||
}
|
||||
int count = 0;
|
||||
for (int i = 0; i < MRT::TARGET_COUNT; i++) {
|
||||
if (mColor[i].format != VK_FORMAT_UNDEFINED) {
|
||||
++count;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
bool VulkanRenderTarget::invalidate() {
|
||||
if (!mOffscreen && getSwapContext(mContext).invalid) {
|
||||
getSwapContext(mContext).invalid = false;
|
||||
|
||||
@@ -32,21 +32,18 @@ struct VulkanProgram : public HwProgram {
|
||||
Program::SamplerGroupInfo samplerGroupInfo;
|
||||
};
|
||||
|
||||
struct VulkanTexture;
|
||||
|
||||
// The render target bundles together a set of attachments, each of which can have one of the
|
||||
// following ownership semantics:
|
||||
//
|
||||
// - The attachment's VkImage is shared and the owner is VulkanSwapChain (mOffScreen = false).
|
||||
// - The attachment's VkImage is shared and the owner is VulkanTexture (mOffScreen = true).
|
||||
// - The attachment's VkImage is shared and the owner is VulkanSwapChain (mOffscreen = false).
|
||||
// - The attachment's VkImage is shared and the owner is VulkanTexture (mOffscreen = true).
|
||||
//
|
||||
// We use private inheritance to shield clients from the width / height fields in HwRenderTarget,
|
||||
// which are not representative when this is the default render target.
|
||||
struct VulkanRenderTarget : private HwRenderTarget {
|
||||
|
||||
// Creates an offscreen render target.
|
||||
VulkanRenderTarget(VulkanContext& context, uint32_t w, uint32_t h, TargetBufferInfo colorInfo,
|
||||
VulkanTexture* color, TargetBufferInfo depthInfo, VulkanTexture* depth);
|
||||
VulkanRenderTarget(VulkanContext& context, uint32_t width, uint32_t height,
|
||||
VulkanAttachment color[MRT::TARGET_COUNT], VulkanAttachment depthStencil[2]);
|
||||
|
||||
// Creates a special "default" render target (i.e. associated with the swap chain)
|
||||
explicit VulkanRenderTarget(VulkanContext& context);
|
||||
@@ -56,18 +53,15 @@ struct VulkanRenderTarget : private HwRenderTarget {
|
||||
void transformClientRectToPlatform(VkRect2D* bounds) const;
|
||||
void transformClientRectToPlatform(VkViewport* bounds) const;
|
||||
VkExtent2D getExtent() const;
|
||||
VulkanAttachment getColor() const;
|
||||
VulkanAttachment getColor(int target) const;
|
||||
VulkanAttachment getDepth() const;
|
||||
int getColorTargetCount() const;
|
||||
bool invalidate();
|
||||
uint32_t getColorLevel() const { return mColorLevel; }
|
||||
uint32_t getDepthLevel() const { return mDepthLevel; }
|
||||
private:
|
||||
VulkanAttachment mColor = {};
|
||||
VulkanAttachment mColor[MRT::TARGET_COUNT] = {};
|
||||
VulkanAttachment mDepth = {};
|
||||
VulkanContext& mContext;
|
||||
bool mOffscreen;
|
||||
uint32_t mColorLevel;
|
||||
uint32_t mDepthLevel;
|
||||
};
|
||||
|
||||
struct VulkanSwapChain : public HwSwapChain {
|
||||
|
||||
Reference in New Issue
Block a user