Commit 54ec759a by Alexis Hetu Committed by Alexis Hétu

Renderpass multisampling resolve

According to the Vulkan spec: "If pResolveAttachments is not NULL, each of its elements corresponds to a color attachment (the element in pColorAttachments at the same index), and a multisample resolve operation is defined for each attachment. At the end of each subpass, multisample resolve operations read the subpass’s color attachments, and resolve the samples for each pixel to the same pixel location in the corresponding resolve attachments, unless the resolve attachment index is VK_ATTACHMENT_UNUSED." Note: This cl adds support for multisampling, but requires syncing before performing the resolve operation. The intent is for the next cl to move the resolve to Renderer::finishRendering(), in order to avoid having to sync when it's not necessary. Bug b/119620965 Change-Id: Id4fae41347e354b822d089fb5b6d4e36592c146b Tests: dEQP-VK.pipeline.multisample.raster_samples.* Tests: dEQP-VK.pipeline.multisample.raster_samples_consistency.* Tests: dEQP-VK.pipeline.multisample.sample_mask.* Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/27650Tested-by: 's avatarAlexis Hétu <sugoi@google.com> Presubmit-Ready: Alexis Hétu <sugoi@google.com> Reviewed-by: 's avatarNicolas Capens <nicolascapens@google.com> Reviewed-by: 's avatarChris Forbes <chrisforbes@google.com> Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
parent fb6639fb
...@@ -47,7 +47,7 @@ namespace sw ...@@ -47,7 +47,7 @@ namespace sw
return; return;
} }
State state(format, dest->getFormat(aspect), dest->getSampleCountFlagBits(), { 0xF }); State state(format, dest->getFormat(aspect), 1, dest->getSampleCountFlagBits(), { 0xF });
Routine *blitRoutine = getRoutine(state); Routine *blitRoutine = getRoutine(state);
if(!blitRoutine) if(!blitRoutine)
{ {
...@@ -89,6 +89,7 @@ namespace sw ...@@ -89,6 +89,7 @@ namespace sw
format.bytes(), // sPitchB format.bytes(), // sPitchB
dest->rowPitchBytes(aspect, subresLayers.mipLevel), // dPitchB dest->rowPitchBytes(aspect, subresLayers.mipLevel), // dPitchB
0, // sSliceB (unused in clear operations)
dest->slicePitchBytes(aspect, subresLayers.mipLevel), // dSliceB dest->slicePitchBytes(aspect, subresLayers.mipLevel), // dSliceB
0.5f, 0.5f, 0.0f, 0.0f, // x0, y0, w, h 0.5f, 0.5f, 0.0f, 0.0f, // x0, y0, w, h
...@@ -1413,6 +1414,21 @@ namespace sw ...@@ -1413,6 +1414,21 @@ namespace sw
{ {
return nullptr; return nullptr;
} }
if(state.srcSamples > 1) // Resolve multisampled source
{
Float4 accum = color;
for(int i = 1; i < state.srcSamples; i++)
{
s += *Pointer<Int>(blit + OFFSET(BlitData, sSliceB));
if(!read(color, s, state))
{
return nullptr;
}
accum += color;
}
color = accum * Float4(1.0f / static_cast<float>(state.srcSamples));
}
} }
else // Bilinear filtering else // Bilinear filtering
{ {
...@@ -1551,7 +1567,7 @@ namespace sw ...@@ -1551,7 +1567,7 @@ namespace sw
float y0 = region.srcOffsets[0].y + (0.5f - region.dstOffsets[0].y) * heightRatio; float y0 = region.srcOffsets[0].y + (0.5f - region.dstOffsets[0].y) * heightRatio;
bool doFilter = (filter != VK_FILTER_NEAREST); bool doFilter = (filter != VK_FILTER_NEAREST);
State state(src->getFormat(srcAspect), dst->getFormat(dstAspect), dst->getSampleCountFlagBits(), State state(src->getFormat(srcAspect), dst->getFormat(dstAspect), src->getSampleCountFlagBits(), dst->getSampleCountFlagBits(),
{ doFilter, srcAspect == VK_IMAGE_ASPECT_STENCIL_BIT, doFilter }); { doFilter, srcAspect == VK_IMAGE_ASPECT_STENCIL_BIT, doFilter });
state.clampToEdge = (region.srcOffsets[0].x < 0) || state.clampToEdge = (region.srcOffsets[0].x < 0) ||
(region.srcOffsets[0].y < 0) || (region.srcOffsets[0].y < 0) ||
...@@ -1573,6 +1589,7 @@ namespace sw ...@@ -1573,6 +1589,7 @@ namespace sw
nullptr, // dest nullptr, // dest
src->rowPitchBytes(srcAspect, region.srcSubresource.mipLevel), // sPitchB src->rowPitchBytes(srcAspect, region.srcSubresource.mipLevel), // sPitchB
dst->rowPitchBytes(dstAspect, region.dstSubresource.mipLevel), // dPitchB dst->rowPitchBytes(dstAspect, region.dstSubresource.mipLevel), // dPitchB
src->slicePitchBytes(srcAspect, region.srcSubresource.mipLevel), // sSliceB
dst->slicePitchBytes(dstAspect, region.dstSubresource.mipLevel), // dSliceB dst->slicePitchBytes(dstAspect, region.dstSubresource.mipLevel), // dSliceB
x0, x0,
......
...@@ -63,8 +63,8 @@ namespace sw ...@@ -63,8 +63,8 @@ namespace sw
{ {
State() = default; State() = default;
State(const Options &options) : Options(options) {} State(const Options &options) : Options(options) {}
State(vk::Format sourceFormat, vk::Format destFormat, int destSamples, const Options &options) : State(vk::Format sourceFormat, vk::Format destFormat, int srcSamples, int destSamples, const Options &options) :
Options(options), sourceFormat(sourceFormat), destFormat(destFormat), destSamples(destSamples) {} Options(options), sourceFormat(sourceFormat), destFormat(destFormat), srcSamples(srcSamples), destSamples(destSamples) {}
bool operator==(const State &state) const bool operator==(const State &state) const
{ {
...@@ -73,6 +73,7 @@ namespace sw ...@@ -73,6 +73,7 @@ namespace sw
vk::Format sourceFormat; vk::Format sourceFormat;
vk::Format destFormat; vk::Format destFormat;
int srcSamples = 0;
int destSamples = 0; int destSamples = 0;
}; };
...@@ -82,6 +83,7 @@ namespace sw ...@@ -82,6 +83,7 @@ namespace sw
void *dest; void *dest;
int sPitchB; int sPitchB;
int dPitchB; int dPitchB;
int sSliceB;
int dSliceB; int dSliceB;
float x0; float x0;
......
...@@ -60,7 +60,7 @@ protected: ...@@ -60,7 +60,7 @@ protected:
executionState.renderPass = renderPass; executionState.renderPass = renderPass;
executionState.renderPassFramebuffer = framebuffer; executionState.renderPassFramebuffer = framebuffer;
renderPass->begin(); renderPass->begin();
framebuffer->clear(clearValueCount, clearValues, renderArea); framebuffer->clear(executionState.renderPass, clearValueCount, clearValues, renderArea);
} }
private: private:
...@@ -81,6 +81,16 @@ public: ...@@ -81,6 +81,16 @@ public:
protected: protected:
void play(CommandBuffer::ExecutionState& executionState) override void play(CommandBuffer::ExecutionState& executionState) override
{ {
bool hasResolveAttachments = (executionState.renderPass->getCurrentSubpass().pResolveAttachments != nullptr);
if(hasResolveAttachments)
{
// FIXME(sugoi): remove the following lines and resolve in Renderer::finishRendering()
// for a Draw command or after the last command of the current subpass
// which modifies pixels.
executionState.renderer->synchronize();
executionState.renderPassFramebuffer->resolve(executionState.renderPass);
}
executionState.renderPass->nextSubpass(); executionState.renderPass->nextSubpass();
} }
...@@ -97,13 +107,18 @@ public: ...@@ -97,13 +107,18 @@ public:
protected: protected:
void play(CommandBuffer::ExecutionState& executionState) override void play(CommandBuffer::ExecutionState& executionState) override
{ {
executionState.renderPass->end();
executionState.renderPass = nullptr;
executionState.renderPassFramebuffer = nullptr;
// Execute (implicit or explicit) VkSubpassDependency to VK_SUBPASS_EXTERNAL // Execute (implicit or explicit) VkSubpassDependency to VK_SUBPASS_EXTERNAL
// This is somewhat heavier than the actual ordering required. // This is somewhat heavier than the actual ordering required.
executionState.renderer->synchronize(); executionState.renderer->synchronize();
// FIXME(sugoi): remove the following line and resolve in Renderer::finishRendering()
// for a Draw command or after the last command of the current subpass
// which modifies pixels.
executionState.renderPassFramebuffer->resolve(executionState.renderPass);
executionState.renderPass->end();
executionState.renderPass = nullptr;
executionState.renderPassFramebuffer = nullptr;
} }
private: private:
...@@ -506,7 +521,7 @@ struct ClearAttachment : public CommandBuffer::Command ...@@ -506,7 +521,7 @@ struct ClearAttachment : public CommandBuffer::Command
void play(CommandBuffer::ExecutionState& executionState) override void play(CommandBuffer::ExecutionState& executionState) override
{ {
executionState.renderPassFramebuffer->clear(attachment, rect); executionState.renderPassFramebuffer->clear(executionState.renderPass, attachment, rect);
} }
private: private:
......
...@@ -22,7 +22,6 @@ namespace vk ...@@ -22,7 +22,6 @@ namespace vk
{ {
Framebuffer::Framebuffer(const VkFramebufferCreateInfo* pCreateInfo, void* mem) : Framebuffer::Framebuffer(const VkFramebufferCreateInfo* pCreateInfo, void* mem) :
renderPass(Cast(pCreateInfo->renderPass)),
attachmentCount(pCreateInfo->attachmentCount), attachmentCount(pCreateInfo->attachmentCount),
attachments(reinterpret_cast<ImageView**>(mem)) attachments(reinterpret_cast<ImageView**>(mem))
{ {
...@@ -37,7 +36,7 @@ void Framebuffer::destroy(const VkAllocationCallbacks* pAllocator) ...@@ -37,7 +36,7 @@ void Framebuffer::destroy(const VkAllocationCallbacks* pAllocator)
vk::deallocate(attachments, pAllocator); vk::deallocate(attachments, pAllocator);
} }
void Framebuffer::clear(uint32_t clearValueCount, const VkClearValue* pClearValues, const VkRect2D& renderArea) void Framebuffer::clear(const RenderPass* renderPass, uint32_t clearValueCount, const VkClearValue* pClearValues, const VkRect2D& renderArea)
{ {
ASSERT(attachmentCount == renderPass->getAttachmentCount()); ASSERT(attachmentCount == renderPass->getAttachmentCount());
...@@ -69,7 +68,7 @@ void Framebuffer::clear(uint32_t clearValueCount, const VkClearValue* pClearValu ...@@ -69,7 +68,7 @@ void Framebuffer::clear(uint32_t clearValueCount, const VkClearValue* pClearValu
} }
} }
void Framebuffer::clear(const VkClearAttachment& attachment, const VkClearRect& rect) void Framebuffer::clear(const RenderPass* renderPass, const VkClearAttachment& attachment, const VkClearRect& rect)
{ {
if(attachment.aspectMask == VK_IMAGE_ASPECT_COLOR_BIT) if(attachment.aspectMask == VK_IMAGE_ASPECT_COLOR_BIT)
{ {
...@@ -99,6 +98,22 @@ ImageView *Framebuffer::getAttachment(uint32_t index) const ...@@ -99,6 +98,22 @@ ImageView *Framebuffer::getAttachment(uint32_t index) const
return attachments[index]; return attachments[index];
} }
void Framebuffer::resolve(const RenderPass* renderPass)
{
VkSubpassDescription subpass = renderPass->getCurrentSubpass();
if(subpass.pResolveAttachments)
{
for(uint32_t i = 0; i < subpass.colorAttachmentCount; i++)
{
uint32_t resolveAttachment = subpass.pResolveAttachments[i].attachment;
if(resolveAttachment != VK_ATTACHMENT_UNUSED)
{
attachments[subpass.pColorAttachments[i].attachment]->resolve(attachments[resolveAttachment]);
}
}
}
}
size_t Framebuffer::ComputeRequiredAllocationSize(const VkFramebufferCreateInfo* pCreateInfo) size_t Framebuffer::ComputeRequiredAllocationSize(const VkFramebufferCreateInfo* pCreateInfo)
{ {
return pCreateInfo->attachmentCount * sizeof(void*); return pCreateInfo->attachmentCount * sizeof(void*);
......
...@@ -30,14 +30,14 @@ public: ...@@ -30,14 +30,14 @@ public:
~Framebuffer() = delete; ~Framebuffer() = delete;
void destroy(const VkAllocationCallbacks* pAllocator); void destroy(const VkAllocationCallbacks* pAllocator);
void clear(uint32_t clearValueCount, const VkClearValue* pClearValues, const VkRect2D& renderArea); void clear(const RenderPass* renderPass, uint32_t clearValueCount, const VkClearValue* pClearValues, const VkRect2D& renderArea);
void clear(const VkClearAttachment& attachment, const VkClearRect& rect); void clear(const RenderPass* renderPass, const VkClearAttachment& attachment, const VkClearRect& rect);
static size_t ComputeRequiredAllocationSize(const VkFramebufferCreateInfo* pCreateInfo); static size_t ComputeRequiredAllocationSize(const VkFramebufferCreateInfo* pCreateInfo);
ImageView *getAttachment(uint32_t index) const; ImageView *getAttachment(uint32_t index) const;
void resolve(const RenderPass* renderPass);
private: private:
RenderPass* renderPass;
uint32_t attachmentCount = 0; uint32_t attachmentCount = 0;
ImageView** attachments = nullptr; ImageView** attachments = nullptr;
}; };
......
...@@ -52,10 +52,6 @@ Image::Image(const Image::CreateInfo* pCreateInfo, void* mem) : ...@@ -52,10 +52,6 @@ Image::Image(const Image::CreateInfo* pCreateInfo, void* mem) :
samples(pCreateInfo->pCreateInfo->samples), samples(pCreateInfo->pCreateInfo->samples),
tiling(pCreateInfo->pCreateInfo->tiling) tiling(pCreateInfo->pCreateInfo->tiling)
{ {
if (samples != VK_SAMPLE_COUNT_1_BIT)
{
UNIMPLEMENTED("Multisample images not yet supported");
}
} }
void Image::destroy(const VkAllocationCallbacks* pAllocator) void Image::destroy(const VkAllocationCallbacks* pAllocator)
...@@ -93,7 +89,7 @@ void Image::getSubresourceLayout(const VkImageSubresource* pSubresource, VkSubre ...@@ -93,7 +89,7 @@ void Image::getSubresourceLayout(const VkImageSubresource* pSubresource, VkSubre
} }
auto aspect = static_cast<VkImageAspectFlagBits>(pSubresource->aspectMask); auto aspect = static_cast<VkImageAspectFlagBits>(pSubresource->aspectMask);
pLayout->offset = getMemoryOffset(aspect, pSubresource->mipLevel, pSubresource->arrayLayer); pLayout->offset = getMemoryOffset(aspect, pSubresource->mipLevel, pSubresource->arrayLayer);
pLayout->size = getMipLevelSize(aspect, pSubresource->mipLevel); pLayout->size = getMultiSampledLevelSize(aspect, pSubresource->mipLevel);
pLayout->rowPitch = rowPitchBytes(aspect, pSubresource->mipLevel); pLayout->rowPitch = rowPitchBytes(aspect, pSubresource->mipLevel);
pLayout->depthPitch = slicePitchBytes(aspect, pSubresource->mipLevel); pLayout->depthPitch = slicePitchBytes(aspect, pSubresource->mipLevel);
pLayout->arrayPitch = getLayerSize(aspect); pLayout->arrayPitch = getLayerSize(aspect);
...@@ -123,6 +119,25 @@ void Image::copyTo(VkImage dstImage, const VkImageCopy& pRegion) ...@@ -123,6 +119,25 @@ void Image::copyTo(VkImage dstImage, const VkImageCopy& pRegion)
UNIMPLEMENTED("dstSubresource"); UNIMPLEMENTED("dstSubresource");
} }
if((samples > VK_SAMPLE_COUNT_1_BIT) && (imageType == VK_IMAGE_TYPE_2D) && !format.isNonNormalizedInteger())
{
// Requires multisampling resolve
VkImageBlit region;
region.srcSubresource = pRegion.srcSubresource;
region.srcOffsets[0] = pRegion.srcOffset;
region.srcOffsets[1].x = region.srcOffsets[0].x + pRegion.extent.width;
region.srcOffsets[1].y = region.srcOffsets[0].y + pRegion.extent.height;
region.srcOffsets[1].z = region.srcOffsets[0].z + pRegion.extent.depth;
region.dstSubresource = pRegion.dstSubresource;
region.dstOffsets[0] = pRegion.dstOffset;
region.dstOffsets[1].x = region.dstOffsets[0].x + pRegion.extent.width;
region.dstOffsets[1].y = region.dstOffsets[0].y + pRegion.extent.height;
region.dstOffsets[1].z = region.dstOffsets[0].z + pRegion.extent.depth;
return device->getBlitter()->blit(this, dst, region, VK_FILTER_NEAREST);
}
VkImageAspectFlagBits srcAspect = static_cast<VkImageAspectFlagBits>(pRegion.srcSubresource.aspectMask); VkImageAspectFlagBits srcAspect = static_cast<VkImageAspectFlagBits>(pRegion.srcSubresource.aspectMask);
VkImageAspectFlagBits dstAspect = static_cast<VkImageAspectFlagBits>(pRegion.dstSubresource.aspectMask); VkImageAspectFlagBits dstAspect = static_cast<VkImageAspectFlagBits>(pRegion.dstSubresource.aspectMask);
...@@ -239,10 +254,7 @@ void Image::copy(VkBuffer buf, const VkBufferImageCopy& region, bool bufferIsSou ...@@ -239,10 +254,7 @@ void Image::copy(VkBuffer buf, const VkBufferImageCopy& region, bool bufferIsSou
Buffer* buffer = Cast(buf); Buffer* buffer = Cast(buf);
uint8_t* bufferMemory = static_cast<uint8_t*>(buffer->getOffsetPointer(region.bufferOffset)); uint8_t* bufferMemory = static_cast<uint8_t*>(buffer->getOffsetPointer(region.bufferOffset));
uint8_t* imageMemory = static_cast<uint8_t*>(deviceMemory->getOffsetPointer( uint8_t* imageMemory = static_cast<uint8_t*>(getTexelPointer(region.imageOffset, region.imageSubresource));
getMemoryOffset(aspect, region.imageSubresource.mipLevel,
region.imageSubresource.baseArrayLayer) +
texelOffsetBytesInStorage(region.imageOffset, region.imageSubresource)));
uint8_t* srcMemory = bufferIsSource ? bufferMemory : imageMemory; uint8_t* srcMemory = bufferIsSource ? bufferMemory : imageMemory;
uint8_t* dstMemory = bufferIsSource ? imageMemory : bufferMemory; uint8_t* dstMemory = bufferIsSource ? imageMemory : bufferMemory;
...@@ -467,7 +479,7 @@ VkDeviceSize Image::getMemoryOffset(VkImageAspectFlagBits aspect, uint32_t mipLe ...@@ -467,7 +479,7 @@ VkDeviceSize Image::getMemoryOffset(VkImageAspectFlagBits aspect, uint32_t mipLe
VkDeviceSize offset = getMemoryOffset(aspect); VkDeviceSize offset = getMemoryOffset(aspect);
for(uint32_t i = 0; i < mipLevel; ++i) for(uint32_t i = 0; i < mipLevel; ++i)
{ {
offset += getMipLevelSize(aspect, i); offset += getMultiSampledLevelSize(aspect, i);
} }
return offset; return offset;
} }
...@@ -482,13 +494,18 @@ VkDeviceSize Image::getMipLevelSize(VkImageAspectFlagBits aspect, uint32_t mipLe ...@@ -482,13 +494,18 @@ VkDeviceSize Image::getMipLevelSize(VkImageAspectFlagBits aspect, uint32_t mipLe
return getMipLevelExtent(mipLevel).depth * slicePitchBytes(aspect, mipLevel); return getMipLevelExtent(mipLevel).depth * slicePitchBytes(aspect, mipLevel);
} }
VkDeviceSize Image::getMultiSampledLevelSize(VkImageAspectFlagBits aspect, uint32_t mipLevel) const
{
return getMipLevelSize(aspect, mipLevel) * samples;
}
VkDeviceSize Image::getLayerSize(VkImageAspectFlagBits aspect) const VkDeviceSize Image::getLayerSize(VkImageAspectFlagBits aspect) const
{ {
VkDeviceSize layerSize = 0; VkDeviceSize layerSize = 0;
for(uint32_t mipLevel = 0; mipLevel < mipLevels; ++mipLevel) for(uint32_t mipLevel = 0; mipLevel < mipLevels; ++mipLevel)
{ {
layerSize += getMipLevelSize(aspect, mipLevel); layerSize += getMultiSampledLevelSize(aspect, mipLevel);
} }
return layerSize; return layerSize;
......
...@@ -70,6 +70,7 @@ private: ...@@ -70,6 +70,7 @@ private:
void copy(VkBuffer buffer, const VkBufferImageCopy& region, bool bufferIsSource); void copy(VkBuffer buffer, const VkBufferImageCopy& region, bool bufferIsSource);
VkDeviceSize getStorageSize(VkImageAspectFlags flags) const; VkDeviceSize getStorageSize(VkImageAspectFlags flags) const;
VkDeviceSize getMipLevelSize(VkImageAspectFlagBits aspect, uint32_t mipLevel) const; VkDeviceSize getMipLevelSize(VkImageAspectFlagBits aspect, uint32_t mipLevel) const;
VkDeviceSize getMultiSampledLevelSize(VkImageAspectFlagBits aspect, uint32_t mipLevel) const;
VkDeviceSize getLayerSize(VkImageAspectFlagBits aspect) const; VkDeviceSize getLayerSize(VkImageAspectFlagBits aspect) const;
VkDeviceSize getMemoryOffset(VkImageAspectFlagBits aspect, uint32_t mipLevel) const; VkDeviceSize getMemoryOffset(VkImageAspectFlagBits aspect, uint32_t mipLevel) const;
VkDeviceSize getMemoryOffset(VkImageAspectFlagBits aspect, uint32_t mipLevel, uint32_t layer) const; VkDeviceSize getMemoryOffset(VkImageAspectFlagBits aspect, uint32_t mipLevel, uint32_t layer) const;
......
...@@ -121,6 +121,35 @@ void ImageView::clear(const VkClearValue& clearValue, const VkImageAspectFlags a ...@@ -121,6 +121,35 @@ void ImageView::clear(const VkClearValue& clearValue, const VkImageAspectFlags a
image->clear(clearValue, renderArea.rect, sr); image->clear(clearValue, renderArea.rect, sr);
} }
void ImageView::resolve(ImageView* resolveAttachment)
{
if((subresourceRange.levelCount != 1) || (resolveAttachment->subresourceRange.levelCount != 1))
{
UNIMPLEMENTED("levelCount");
}
VkImageCopy region;
region.srcSubresource =
{
subresourceRange.aspectMask,
subresourceRange.baseMipLevel,
subresourceRange.baseArrayLayer,
subresourceRange.layerCount
};
region.srcOffset = { 0, 0, 0 };
region.dstSubresource =
{
resolveAttachment->subresourceRange.aspectMask,
resolveAttachment->subresourceRange.baseMipLevel,
resolveAttachment->subresourceRange.baseArrayLayer,
resolveAttachment->subresourceRange.layerCount
};
region.dstOffset = { 0, 0, 0 };
region.extent = image->getMipLevelExtent(subresourceRange.baseMipLevel);
image->copyTo(*(resolveAttachment->image), region);
}
void *ImageView::getOffsetPointer(const VkOffset3D& offset, VkImageAspectFlagBits aspect) const void *ImageView::getOffsetPointer(const VkOffset3D& offset, VkImageAspectFlagBits aspect) const
{ {
VkImageSubresourceLayers imageSubresourceLayers = VkImageSubresourceLayers imageSubresourceLayers =
......
...@@ -34,6 +34,7 @@ public: ...@@ -34,6 +34,7 @@ public:
void clear(const VkClearValue& clearValues, VkImageAspectFlags aspectMask, const VkRect2D& renderArea); void clear(const VkClearValue& clearValues, VkImageAspectFlags aspectMask, const VkRect2D& renderArea);
void clear(const VkClearValue& clearValue, VkImageAspectFlags aspectMask, const VkClearRect& renderArea); void clear(const VkClearValue& clearValue, VkImageAspectFlags aspectMask, const VkClearRect& renderArea);
void resolve(ImageView* resolveAttachment);
Format getFormat() const { return format; } Format getFormat() const { return format; }
int getSampleCount() const { return image->getSampleCountFlagBits(); } int getSampleCount() const { return image->getSampleCountFlagBits(); }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment