Commit 02d4c0d3 by Chris Forbes

Add support for multiview rendering

This is a mandatory feature for 1.1, but Vulkan CTS 1.1.3 doesn't enforce that -- it only tests the feature if it is present. Highlights: - Multiple views implemented by running each draw multiple times. We could do something more efficient, but this is a fringe feature so far. - Render targets and input attachments are adjusted to use the layer corresponding to the current view. - Explicit attachment clears and end-of-subpass resolves are broadcast to the layers corresponding to the current subpass's view mask. - Renderpass attachment load ops are executed for the layers corresponding to the union of the subpass view masks for all subpasses which use the attachment. The actual load ops are still performed together at the beginning of the renderpass. - ViewIndex builtin variable is exposed to the shaders. In a non-multiview draw call, ViewIndex is still available, but is always zero. Bug: b/139862810 Test: dEQP-VK.*multiview* Change-Id: Iaf40cfdb2f5afa61253cc756f97c0db30fb4d813 Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/35408 Kokoro-Presubmit: kokoro <noreply+kokoro@google.com> Reviewed-by: 's avatarNicolas Capens <nicolascapens@google.com> Tested-by: 's avatarChris Forbes <chrisforbes@google.com>
parent 23742c09
...@@ -96,6 +96,7 @@ namespace sw ...@@ -96,6 +96,7 @@ namespace sw
// Instancing // Instancing
int instanceID; int instanceID;
int viewID;
bool occlusionEnabled; bool occlusionEnabled;
......
...@@ -348,11 +348,8 @@ namespace sw ...@@ -348,11 +348,8 @@ namespace sw
} }
data->indices = context->indexBuffer; data->indices = context->indexBuffer;
data->viewID = context->viewID;
if(context->vertexShader->hasBuiltinInput(spv::BuiltInInstanceIndex)) data->instanceID = context->instanceID;
{
data->instanceID = context->instanceID;
}
data->baseVertex = baseVertex; data->baseVertex = baseVertex;
...@@ -426,7 +423,7 @@ namespace sw ...@@ -426,7 +423,7 @@ namespace sw
if(draw->renderTarget[index]) if(draw->renderTarget[index])
{ {
data->colorBuffer[index] = (unsigned int*)context->renderTarget[index]->getOffsetPointer({0, 0, 0}, VK_IMAGE_ASPECT_COLOR_BIT, 0, 0); data->colorBuffer[index] = (unsigned int*)context->renderTarget[index]->getOffsetPointer({0, 0, 0}, VK_IMAGE_ASPECT_COLOR_BIT, 0, data->viewID);
data->colorPitchB[index] = context->renderTarget[index]->rowPitchBytes(VK_IMAGE_ASPECT_COLOR_BIT, 0); data->colorPitchB[index] = context->renderTarget[index]->rowPitchBytes(VK_IMAGE_ASPECT_COLOR_BIT, 0);
data->colorSliceB[index] = context->renderTarget[index]->slicePitchBytes(VK_IMAGE_ASPECT_COLOR_BIT, 0); data->colorSliceB[index] = context->renderTarget[index]->slicePitchBytes(VK_IMAGE_ASPECT_COLOR_BIT, 0);
} }
...@@ -437,14 +434,14 @@ namespace sw ...@@ -437,14 +434,14 @@ namespace sw
if(draw->depthBuffer) if(draw->depthBuffer)
{ {
data->depthBuffer = (float*)context->depthBuffer->getOffsetPointer({0, 0, 0}, VK_IMAGE_ASPECT_DEPTH_BIT, 0, 0); data->depthBuffer = (float*)context->depthBuffer->getOffsetPointer({0, 0, 0}, VK_IMAGE_ASPECT_DEPTH_BIT, 0, data->viewID);
data->depthPitchB = context->depthBuffer->rowPitchBytes(VK_IMAGE_ASPECT_DEPTH_BIT, 0); data->depthPitchB = context->depthBuffer->rowPitchBytes(VK_IMAGE_ASPECT_DEPTH_BIT, 0);
data->depthSliceB = context->depthBuffer->slicePitchBytes(VK_IMAGE_ASPECT_DEPTH_BIT, 0); data->depthSliceB = context->depthBuffer->slicePitchBytes(VK_IMAGE_ASPECT_DEPTH_BIT, 0);
} }
if(draw->stencilBuffer) if(draw->stencilBuffer)
{ {
data->stencilBuffer = (unsigned char*)context->stencilBuffer->getOffsetPointer({0, 0, 0}, VK_IMAGE_ASPECT_STENCIL_BIT, 0, 0); data->stencilBuffer = (unsigned char*)context->stencilBuffer->getOffsetPointer({0, 0, 0}, VK_IMAGE_ASPECT_STENCIL_BIT, 0, data->viewID);
data->stencilPitchB = context->stencilBuffer->rowPitchBytes(VK_IMAGE_ASPECT_STENCIL_BIT, 0); data->stencilPitchB = context->stencilBuffer->rowPitchBytes(VK_IMAGE_ASPECT_STENCIL_BIT, 0);
data->stencilSliceB = context->stencilBuffer->slicePitchBytes(VK_IMAGE_ASPECT_STENCIL_BIT, 0); data->stencilSliceB = context->stencilBuffer->slicePitchBytes(VK_IMAGE_ASPECT_STENCIL_BIT, 0);
} }
......
...@@ -60,6 +60,7 @@ namespace sw ...@@ -60,6 +60,7 @@ namespace sw
int instanceID; int instanceID;
int baseVertex; int baseVertex;
float lineWidth; float lineWidth;
int viewID;
PixelProcessor::Stencil stencil[2]; // clockwise, counterclockwise PixelProcessor::Stencil stencil[2]; // clockwise, counterclockwise
PixelProcessor::Factor factor; PixelProcessor::Factor factor;
......
...@@ -24,6 +24,12 @@ namespace sw ...@@ -24,6 +24,12 @@ namespace sw
{ {
routine.setImmutableInputBuiltins(spirvShader); routine.setImmutableInputBuiltins(spirvShader);
routine.setInputBuiltin(spirvShader, spv::BuiltInViewIndex, [&](const SpirvShader::BuiltinMapping& builtin, Array<SIMD::Float>& value)
{
assert(builtin.SizeInComponents == 1);
value[builtin.FirstComponent] = As<Float4>(Int4((*Pointer<Int>(data + OFFSET(DrawData, viewID)))));
});
routine.setInputBuiltin(spirvShader, spv::BuiltInFragCoord, [&](const SpirvShader::BuiltinMapping& builtin, Array<SIMD::Float>& value) routine.setInputBuiltin(spirvShader, spv::BuiltInFragCoord, [&](const SpirvShader::BuiltinMapping& builtin, Array<SIMD::Float>& value)
{ {
assert(builtin.SizeInComponents == 4); assert(builtin.SizeInComponents == 4);
...@@ -50,6 +56,7 @@ namespace sw ...@@ -50,6 +56,7 @@ namespace sw
routine.windowSpacePosition[0] = x + SIMD::Int(0,1,0,1); routine.windowSpacePosition[0] = x + SIMD::Int(0,1,0,1);
routine.windowSpacePosition[1] = y + SIMD::Int(0,0,1,1); routine.windowSpacePosition[1] = y + SIMD::Int(0,0,1,1);
routine.viewID = *Pointer<Int>(data + OFFSET(DrawData, viewID));
} }
void PixelProgram::applyShader(Int cMask[4]) void PixelProgram::applyShader(Int cMask[4])
......
...@@ -1131,6 +1131,7 @@ namespace sw ...@@ -1131,6 +1131,7 @@ namespace sw
if (!strcmp(ext, "SPV_KHR_16bit_storage")) break; if (!strcmp(ext, "SPV_KHR_16bit_storage")) break;
if (!strcmp(ext, "SPV_KHR_variable_pointers")) break; if (!strcmp(ext, "SPV_KHR_variable_pointers")) break;
if (!strcmp(ext, "SPV_KHR_device_group")) break; if (!strcmp(ext, "SPV_KHR_device_group")) break;
if (!strcmp(ext, "SPV_KHR_multiview")) break;
UNSUPPORTED("SPIR-V Extension: %s", ext); UNSUPPORTED("SPIR-V Extension: %s", ext);
break; break;
} }
...@@ -5325,6 +5326,12 @@ namespace sw ...@@ -5325,6 +5326,12 @@ namespace sw
ptr += coordinate.Int(dims) * slicePitch; ptr += coordinate.Int(dims) * slicePitch;
} }
if (dim == spv::DimSubpassData)
{
// Multiview input attachment access is to the layer corresponding to the current view
ptr += SIMD::Int(routine->viewID) * slicePitch;
}
if (sampleId.value()) if (sampleId.value())
{ {
GenericValue sample(this, state, sampleId); GenericValue sample(this, state, sampleId);
......
...@@ -1299,6 +1299,7 @@ namespace sw ...@@ -1299,6 +1299,7 @@ namespace sw
Pointer<Byte> constants; Pointer<Byte> constants;
Int killMask = Int{0}; Int killMask = Int{0};
SIMD::Int windowSpacePosition[2]; SIMD::Int windowSpacePosition[2];
Int viewID; // slice offset into input attachments for multiview, even if the shader doesn't use ViewIndex
void createVariable(SpirvShader::Object::ID id, uint32_t size) void createVariable(SpirvShader::Object::ID id, uint32_t size)
{ {
......
...@@ -34,6 +34,12 @@ namespace sw ...@@ -34,6 +34,12 @@ namespace sw
{ {
routine.setImmutableInputBuiltins(spirvShader); routine.setImmutableInputBuiltins(spirvShader);
routine.setInputBuiltin(spirvShader, spv::BuiltInViewIndex, [&](const SpirvShader::BuiltinMapping& builtin, Array<SIMD::Float>& value)
{
assert(builtin.SizeInComponents == 1);
value[builtin.FirstComponent] = As<Float4>(Int4((*Pointer<Int>(data + OFFSET(DrawData, viewID)))));
});
routine.setInputBuiltin(spirvShader, spv::BuiltInInstanceIndex, [&](const SpirvShader::BuiltinMapping& builtin, Array<SIMD::Float>& value) routine.setInputBuiltin(spirvShader, spv::BuiltInInstanceIndex, [&](const SpirvShader::BuiltinMapping& builtin, Array<SIMD::Float>& value)
{ {
// TODO: we could do better here; we know InstanceIndex is uniform across all lanes // TODO: we could do better here; we know InstanceIndex is uniform across all lanes
......
...@@ -515,7 +515,7 @@ struct DrawBase : public CommandBuffer::Command ...@@ -515,7 +515,7 @@ struct DrawBase : public CommandBuffer::Command
{ {
auto const &pipelineState = executionState.pipelineState[VK_PIPELINE_BIND_POINT_GRAPHICS]; auto const &pipelineState = executionState.pipelineState[VK_PIPELINE_BIND_POINT_GRAPHICS];
GraphicsPipeline* pipeline = static_cast<GraphicsPipeline*>(pipelineState.pipeline); GraphicsPipeline *pipeline = static_cast<GraphicsPipeline *>(pipelineState.pipeline);
sw::Context context = pipeline->getContext(); sw::Context context = pipeline->getContext();
...@@ -527,12 +527,13 @@ struct DrawBase : public CommandBuffer::Command ...@@ -527,12 +527,13 @@ struct DrawBase : public CommandBuffer::Command
// Apply either pipeline state or dynamic state // Apply either pipeline state or dynamic state
executionState.renderer->setScissor(pipeline->hasDynamicState(VK_DYNAMIC_STATE_SCISSOR) ? executionState.renderer->setScissor(pipeline->hasDynamicState(VK_DYNAMIC_STATE_SCISSOR) ?
executionState.dynamicState.scissor : pipeline->getScissor()); executionState.dynamicState.scissor : pipeline->getScissor());
executionState.renderer->setViewport(pipeline->hasDynamicState(VK_DYNAMIC_STATE_VIEWPORT) ? executionState.renderer->setViewport(pipeline->hasDynamicState(VK_DYNAMIC_STATE_VIEWPORT) ?
executionState.dynamicState.viewport : pipeline->getViewport()); executionState.dynamicState.viewport : pipeline->getViewport());
executionState.renderer->setBlendConstant(pipeline->hasDynamicState(VK_DYNAMIC_STATE_BLEND_CONSTANTS) ? executionState.renderer->setBlendConstant(pipeline->hasDynamicState(VK_DYNAMIC_STATE_BLEND_CONSTANTS) ?
executionState.dynamicState.blendConstants : pipeline->getBlendConstants()); executionState.dynamicState.blendConstants
if(pipeline->hasDynamicState(VK_DYNAMIC_STATE_DEPTH_BIAS)) : pipeline->getBlendConstants());
if (pipeline->hasDynamicState(VK_DYNAMIC_STATE_DEPTH_BIAS))
{ {
// If the depth bias clamping feature is not enabled, depthBiasClamp must be 0.0 // If the depth bias clamping feature is not enabled, depthBiasClamp must be 0.0
ASSERT(executionState.dynamicState.depthBiasClamp == 0.0f); ASSERT(executionState.dynamicState.depthBiasClamp == 0.0f);
...@@ -540,25 +541,27 @@ struct DrawBase : public CommandBuffer::Command ...@@ -540,25 +541,27 @@ struct DrawBase : public CommandBuffer::Command
context.depthBias = executionState.dynamicState.depthBiasConstantFactor; context.depthBias = executionState.dynamicState.depthBiasConstantFactor;
context.slopeDepthBias = executionState.dynamicState.depthBiasSlopeFactor; context.slopeDepthBias = executionState.dynamicState.depthBiasSlopeFactor;
} }
if(pipeline->hasDynamicState(VK_DYNAMIC_STATE_DEPTH_BOUNDS) && context.depthBoundsTestEnable) if (pipeline->hasDynamicState(VK_DYNAMIC_STATE_DEPTH_BOUNDS) && context.depthBoundsTestEnable)
{ {
// Unless the VK_EXT_depth_range_unrestricted extension is enabled minDepthBounds and maxDepthBounds must be between 0.0 and 1.0, inclusive // Unless the VK_EXT_depth_range_unrestricted extension is enabled minDepthBounds and maxDepthBounds must be between 0.0 and 1.0, inclusive
ASSERT(executionState.dynamicState.minDepthBounds >= 0.0f && executionState.dynamicState.minDepthBounds <= 1.0f); ASSERT(executionState.dynamicState.minDepthBounds >= 0.0f &&
ASSERT(executionState.dynamicState.maxDepthBounds >= 0.0f && executionState.dynamicState.maxDepthBounds <= 1.0f); executionState.dynamicState.minDepthBounds <= 1.0f);
ASSERT(executionState.dynamicState.maxDepthBounds >= 0.0f &&
executionState.dynamicState.maxDepthBounds <= 1.0f);
UNIMPLEMENTED("depthBoundsTestEnable"); UNIMPLEMENTED("depthBoundsTestEnable");
} }
if(pipeline->hasDynamicState(VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK) && context.stencilEnable) if (pipeline->hasDynamicState(VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK) && context.stencilEnable)
{ {
context.frontStencil.compareMask = executionState.dynamicState.compareMask[0]; context.frontStencil.compareMask = executionState.dynamicState.compareMask[0];
context.backStencil.compareMask = executionState.dynamicState.compareMask[1]; context.backStencil.compareMask = executionState.dynamicState.compareMask[1];
} }
if(pipeline->hasDynamicState(VK_DYNAMIC_STATE_STENCIL_WRITE_MASK) && context.stencilEnable) if (pipeline->hasDynamicState(VK_DYNAMIC_STATE_STENCIL_WRITE_MASK) && context.stencilEnable)
{ {
context.frontStencil.writeMask = executionState.dynamicState.writeMask[0]; context.frontStencil.writeMask = executionState.dynamicState.writeMask[0];
context.backStencil.writeMask = executionState.dynamicState.writeMask[1]; context.backStencil.writeMask = executionState.dynamicState.writeMask[1];
} }
if(pipeline->hasDynamicState(VK_DYNAMIC_STATE_STENCIL_REFERENCE) && context.stencilEnable) if (pipeline->hasDynamicState(VK_DYNAMIC_STATE_STENCIL_REFERENCE) && context.stencilEnable)
{ {
context.frontStencil.reference = executionState.dynamicState.reference[0]; context.frontStencil.reference = executionState.dynamicState.reference[0];
context.backStencil.reference = executionState.dynamicState.reference[1]; context.backStencil.reference = executionState.dynamicState.reference[1];
...@@ -566,23 +569,23 @@ struct DrawBase : public CommandBuffer::Command ...@@ -566,23 +569,23 @@ struct DrawBase : public CommandBuffer::Command
executionState.bindAttachments(context); executionState.bindAttachments(context);
context.multiSampleMask = context.sampleMask & ((unsigned)0xFFFFFFFF >> (32 - context.sampleCount)); context.multiSampleMask = context.sampleMask & ((unsigned) 0xFFFFFFFF >> (32 - context.sampleCount));
context.occlusionEnabled = executionState.renderer->hasOcclusionQuery(); context.occlusionEnabled = executionState.renderer->hasOcclusionQuery();
std::vector<std::pair<uint32_t, void*>> indexBuffers; std::vector<std::pair<uint32_t, void *>> indexBuffers;
if(indexed) if (indexed)
{ {
void* indexBuffer = executionState.indexBufferBinding.buffer->getOffsetPointer( void *indexBuffer = executionState.indexBufferBinding.buffer->getOffsetPointer(
executionState.indexBufferBinding.offset + first * bytesPerIndex(executionState)); executionState.indexBufferBinding.offset + first * bytesPerIndex(executionState));
if(pipeline->hasPrimitiveRestartEnable()) if (pipeline->hasPrimitiveRestartEnable())
{ {
switch(executionState.indexType) switch (executionState.indexType)
{ {
case VK_INDEX_TYPE_UINT16: case VK_INDEX_TYPE_UINT16:
processPrimitiveRestart(static_cast<uint16_t*>(indexBuffer), count, pipeline, indexBuffers); processPrimitiveRestart(static_cast<uint16_t *>(indexBuffer), count, pipeline, indexBuffers);
break; break;
case VK_INDEX_TYPE_UINT32: case VK_INDEX_TYPE_UINT32:
processPrimitiveRestart(static_cast<uint32_t*>(indexBuffer), count, pipeline, indexBuffers); processPrimitiveRestart(static_cast<uint32_t *>(indexBuffer), count, pipeline, indexBuffers);
break; break;
default: default:
UNIMPLEMENTED("executionState.indexType %d", int(executionState.indexType)); UNIMPLEMENTED("executionState.indexType %d", int(executionState.indexType));
...@@ -590,23 +593,32 @@ struct DrawBase : public CommandBuffer::Command ...@@ -590,23 +593,32 @@ struct DrawBase : public CommandBuffer::Command
} }
else else
{ {
indexBuffers.push_back({ pipeline->computePrimitiveCount(count), indexBuffer }); indexBuffers.push_back({pipeline->computePrimitiveCount(count), indexBuffer});
} }
} }
else else
{ {
indexBuffers.push_back({ pipeline->computePrimitiveCount(count), nullptr }); indexBuffers.push_back({pipeline->computePrimitiveCount(count), nullptr});
} }
for(uint32_t instance = firstInstance; instance != firstInstance + instanceCount; instance++) for (uint32_t instance = firstInstance; instance != firstInstance + instanceCount; instance++)
{ {
context.instanceID = instance; context.instanceID = instance;
for(auto indexBuffer : indexBuffers) // FIXME: reconsider instances/views nesting.
auto viewMask = executionState.renderPass->getViewMask();
while (viewMask)
{ {
const uint32_t primitiveCount = indexBuffer.first; context.viewID = sw::log2i(viewMask);
context.indexBuffer = indexBuffer.second; viewMask &= ~(1 << context.viewID);
executionState.renderer->draw(&context, executionState.indexType, primitiveCount, vertexOffset, executionState.events);
for (auto indexBuffer : indexBuffers)
{
const uint32_t primitiveCount = indexBuffer.first;
context.indexBuffer = indexBuffer.second;
executionState.renderer->draw(&context, executionState.indexType, primitiveCount, vertexOffset,
executionState.events);
}
} }
executionState.renderer->advanceInstanceAttributes(context.input); executionState.renderer->advanceInstanceAttributes(context.input);
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include "VkRenderPass.hpp" #include "VkRenderPass.hpp"
#include <algorithm> #include <algorithm>
#include <memory.h> #include <memory.h>
#include <System/Math.hpp>
namespace vk namespace vk
{ {
...@@ -60,15 +61,38 @@ void Framebuffer::clear(const RenderPass* renderPass, uint32_t clearValueCount, ...@@ -60,15 +61,38 @@ void Framebuffer::clear(const RenderPass* renderPass, uint32_t clearValueCount,
if(clearDepth || clearStencil) if(clearDepth || clearStencil)
{ {
attachments[i]->clear(pClearValues[i], auto aspectMask = (clearDepth ? VK_IMAGE_ASPECT_DEPTH_BIT : 0) |
(clearDepth ? VK_IMAGE_ASPECT_DEPTH_BIT : 0) | (clearStencil ? VK_IMAGE_ASPECT_STENCIL_BIT : 0);
(clearStencil ? VK_IMAGE_ASPECT_STENCIL_BIT : 0), if (renderPass->isMultiView())
renderArea); {
auto viewMask = renderPass->getAttachmentViewMask(i);
while (viewMask)
{
uint32_t view = sw::log2i(viewMask);
viewMask &= ~(1 << view);
VkClearRect r{renderArea, view, 1};
attachments[i]->clear(pClearValues[i], aspectMask, r);
}
}
else
attachments[i]->clear(pClearValues[i], aspectMask, renderArea);
} }
} }
else if(attachment.loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR) else if(attachment.loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR)
{ {
attachments[i]->clear(pClearValues[i], VK_IMAGE_ASPECT_COLOR_BIT, renderArea); if (renderPass->isMultiView())
{
auto viewMask = renderPass->getAttachmentViewMask(i);
while (viewMask)
{
uint32_t view = sw::log2i(viewMask);
viewMask &= ~(1 << view);
VkClearRect r{renderArea, view, 1};
attachments[i]->clear(pClearValues[i], VK_IMAGE_ASPECT_COLOR_BIT, r);
}
}
else
attachments[i]->clear(pClearValues[i], VK_IMAGE_ASPECT_COLOR_BIT, renderArea);
} }
} }
} }
...@@ -84,8 +108,21 @@ void Framebuffer::clear(const RenderPass* renderPass, const VkClearAttachment& a ...@@ -84,8 +108,21 @@ void Framebuffer::clear(const RenderPass* renderPass, const VkClearAttachment& a
ASSERT(attachment.colorAttachment < subpass.colorAttachmentCount); ASSERT(attachment.colorAttachment < subpass.colorAttachmentCount);
ASSERT(subpass.pColorAttachments[attachment.colorAttachment].attachment < attachmentCount); ASSERT(subpass.pColorAttachments[attachment.colorAttachment].attachment < attachmentCount);
attachments[subpass.pColorAttachments[attachment.colorAttachment].attachment]->clear( if (renderPass->isMultiView())
attachment.clearValue, attachment.aspectMask, rect); {
auto viewMask = renderPass->getViewMask();
while (viewMask)
{
int view = sw::log2i(viewMask);
viewMask &= ~(1 << view);
VkClearRect r = rect;
r.baseArrayLayer = view;
attachments[subpass.pColorAttachments[attachment.colorAttachment].attachment]->clear(attachment.clearValue, attachment.aspectMask, r);
}
}
else
attachments[subpass.pColorAttachments[attachment.colorAttachment].attachment]->clear(
attachment.clearValue, attachment.aspectMask, rect);
} }
} }
else if(attachment.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) else if(attachment.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT))
...@@ -94,7 +131,20 @@ void Framebuffer::clear(const RenderPass* renderPass, const VkClearAttachment& a ...@@ -94,7 +131,20 @@ void Framebuffer::clear(const RenderPass* renderPass, const VkClearAttachment& a
ASSERT(subpass.pDepthStencilAttachment->attachment < attachmentCount); ASSERT(subpass.pDepthStencilAttachment->attachment < attachmentCount);
attachments[subpass.pDepthStencilAttachment->attachment]->clear(attachment.clearValue, attachment.aspectMask, rect); if (renderPass->isMultiView())
{
auto viewMask = renderPass->getViewMask();
while (viewMask)
{
int view = sw::log2i(viewMask);
viewMask &= ~(1 << view);
VkClearRect r = rect;
r.baseArrayLayer = view;
attachments[subpass.pDepthStencilAttachment->attachment]->clear(attachment.clearValue, attachment.aspectMask, r);
}
}
else
attachments[subpass.pDepthStencilAttachment->attachment]->clear(attachment.clearValue, attachment.aspectMask, rect);
} }
} }
...@@ -113,7 +163,20 @@ void Framebuffer::resolve(const RenderPass* renderPass) ...@@ -113,7 +163,20 @@ void Framebuffer::resolve(const RenderPass* renderPass)
uint32_t resolveAttachment = subpass.pResolveAttachments[i].attachment; uint32_t resolveAttachment = subpass.pResolveAttachments[i].attachment;
if(resolveAttachment != VK_ATTACHMENT_UNUSED) if(resolveAttachment != VK_ATTACHMENT_UNUSED)
{ {
attachments[subpass.pColorAttachments[i].attachment]->resolve(attachments[resolveAttachment]); if (renderPass->isMultiView())
{
auto viewMask = renderPass->getViewMask();
while (viewMask)
{
int view = sw::log2i(viewMask);
viewMask &= ~(1 << view);
attachments[subpass.pColorAttachments[i].attachment]->resolve(attachments[resolveAttachment], view);
}
}
else
{
attachments[subpass.pColorAttachments[i].attachment]->resolve(attachments[resolveAttachment]);
}
} }
} }
} }
......
...@@ -154,6 +154,36 @@ void ImageView::clear(const VkClearValue& clearValue, const VkImageAspectFlags a ...@@ -154,6 +154,36 @@ void ImageView::clear(const VkClearValue& clearValue, const VkImageAspectFlags a
image->clear(clearValue, format, renderArea.rect, sr); image->clear(clearValue, format, renderArea.rect, sr);
} }
void ImageView::resolve(ImageView* resolveAttachment, int layer)
{
if((subresourceRange.levelCount != 1) || (resolveAttachment->subresourceRange.levelCount != 1))
{
UNIMPLEMENTED("levelCount");
}
VkImageCopy region;
region.srcSubresource =
{
subresourceRange.aspectMask,
subresourceRange.baseMipLevel,
subresourceRange.baseArrayLayer + layer,
1
};
region.srcOffset = { 0, 0, 0 };
region.dstSubresource =
{
resolveAttachment->subresourceRange.aspectMask,
resolveAttachment->subresourceRange.baseMipLevel,
resolveAttachment->subresourceRange.baseArrayLayer + layer,
1
};
region.dstOffset = { 0, 0, 0 };
region.extent = image->getMipLevelExtent(static_cast<VkImageAspectFlagBits>(subresourceRange.aspectMask),
subresourceRange.baseMipLevel);
image->copyTo(resolveAttachment->image, region);
}
void ImageView::resolve(ImageView* resolveAttachment) void ImageView::resolve(ImageView* resolveAttachment)
{ {
if((subresourceRange.levelCount != 1) || (resolveAttachment->subresourceRange.levelCount != 1)) if((subresourceRange.levelCount != 1) || (resolveAttachment->subresourceRange.levelCount != 1))
......
...@@ -42,6 +42,7 @@ public: ...@@ -42,6 +42,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); void resolve(ImageView* resolveAttachment);
void resolve(ImageView* resolveAttachment, int layer);
VkImageViewType getType() const { return viewType; } VkImageViewType getType() const { return viewType; }
Format getFormat(Usage usage = RAW) const; Format getFormat(Usage usage = RAW) const;
......
...@@ -119,7 +119,7 @@ void PhysicalDevice::getFeatures(VkPhysicalDevice8BitStorageFeaturesKHR* feature ...@@ -119,7 +119,7 @@ void PhysicalDevice::getFeatures(VkPhysicalDevice8BitStorageFeaturesKHR* feature
void PhysicalDevice::getFeatures(VkPhysicalDeviceMultiviewFeatures* features) const void PhysicalDevice::getFeatures(VkPhysicalDeviceMultiviewFeatures* features) const
{ {
features->multiview = VK_FALSE; features->multiview = VK_TRUE;
features->multiviewGeometryShader = VK_FALSE; features->multiviewGeometryShader = VK_FALSE;
features->multiviewTessellationShader = VK_FALSE; features->multiviewTessellationShader = VK_FALSE;
} }
...@@ -295,8 +295,8 @@ void PhysicalDevice::getProperties(VkPhysicalDeviceMaintenance3Properties* prope ...@@ -295,8 +295,8 @@ void PhysicalDevice::getProperties(VkPhysicalDeviceMaintenance3Properties* prope
void PhysicalDevice::getProperties(VkPhysicalDeviceMultiviewProperties* properties) const void PhysicalDevice::getProperties(VkPhysicalDeviceMultiviewProperties* properties) const
{ {
properties->maxMultiviewViewCount = 0; properties->maxMultiviewViewCount = 6;
properties->maxMultiviewInstanceIndex = 0; properties->maxMultiviewInstanceIndex = 1u<<27;
} }
void PhysicalDevice::getProperties(VkPhysicalDevicePointClippingProperties* properties) const void PhysicalDevice::getProperties(VkPhysicalDevicePointClippingProperties* properties) const
......
...@@ -15,15 +15,6 @@ ...@@ -15,15 +15,6 @@
#include "VkRenderPass.hpp" #include "VkRenderPass.hpp"
#include <cstring> #include <cstring>
namespace
{
void MarkFirstUse(int& attachment, int subpass)
{
if (attachment == -1)
attachment = subpass;
}
}
namespace vk namespace vk
{ {
...@@ -41,6 +32,8 @@ RenderPass::RenderPass(const VkRenderPassCreateInfo* pCreateInfo, void* mem) : ...@@ -41,6 +32,8 @@ RenderPass::RenderPass(const VkRenderPassCreateInfo* pCreateInfo, void* mem) :
subpasses = reinterpret_cast<VkSubpassDescription*>(hostMemory); subpasses = reinterpret_cast<VkSubpassDescription*>(hostMemory);
memcpy(subpasses, pCreateInfo->pSubpasses, subpassesSize); memcpy(subpasses, pCreateInfo->pSubpasses, subpassesSize);
hostMemory += subpassesSize; hostMemory += subpassesSize;
uint32_t *masks = reinterpret_cast<uint32_t *>(hostMemory);
hostMemory += pCreateInfo->subpassCount * sizeof(uint32_t);
if(pCreateInfo->attachmentCount > 0) if(pCreateInfo->attachmentCount > 0)
{ {
...@@ -51,9 +44,43 @@ RenderPass::RenderPass(const VkRenderPassCreateInfo* pCreateInfo, void* mem) : ...@@ -51,9 +44,43 @@ RenderPass::RenderPass(const VkRenderPassCreateInfo* pCreateInfo, void* mem) :
size_t firstUseSize = pCreateInfo->attachmentCount * sizeof(int); size_t firstUseSize = pCreateInfo->attachmentCount * sizeof(int);
attachmentFirstUse = reinterpret_cast<int*>(hostMemory); attachmentFirstUse = reinterpret_cast<int*>(hostMemory);
hostMemory += firstUseSize;
attachmentViewMasks = reinterpret_cast<uint32_t *>(hostMemory);
hostMemory += pCreateInfo->attachmentCount * sizeof(uint32_t);
for (auto i = 0u; i < pCreateInfo->attachmentCount; i++) for (auto i = 0u; i < pCreateInfo->attachmentCount; i++)
{
attachmentFirstUse[i] = -1; attachmentFirstUse[i] = -1;
hostMemory += firstUseSize; attachmentViewMasks[i] = 0;
}
}
const VkBaseInStructure* extensionCreateInfo = reinterpret_cast<const VkBaseInStructure*>(pCreateInfo->pNext);
while (extensionCreateInfo)
{
switch (extensionCreateInfo->sType)
{
case VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO:
{
// Renderpass uses multiview if this structure is present AND some subpass specifies
// a nonzero view mask
auto const *multiviewCreateInfo = reinterpret_cast<VkRenderPassMultiviewCreateInfo const *>(extensionCreateInfo);
for (auto i = 0u; i < pCreateInfo->subpassCount; i++)
{
masks[i] = multiviewCreateInfo->pViewMasks[i];
// This is now a multiview renderpass, so make the masks available
if (masks[i])
viewMasks = masks;
}
break;
}
default:
/* Unknown structure in pNext chain must be ignored */
break;
}
extensionCreateInfo = extensionCreateInfo->pNext;
} }
// Deep copy subpasses // Deep copy subpasses
...@@ -77,7 +104,7 @@ RenderPass::RenderPass(const VkRenderPassCreateInfo* pCreateInfo, void* mem) : ...@@ -77,7 +104,7 @@ RenderPass::RenderPass(const VkRenderPassCreateInfo* pCreateInfo, void* mem) :
for (auto j = 0u; j < subpasses[i].inputAttachmentCount; j++) for (auto j = 0u; j < subpasses[i].inputAttachmentCount; j++)
{ {
if (subpass.pInputAttachments[j].attachment != VK_ATTACHMENT_UNUSED) if (subpass.pInputAttachments[j].attachment != VK_ATTACHMENT_UNUSED)
MarkFirstUse(attachmentFirstUse[subpass.pInputAttachments[j].attachment], i); MarkFirstUse(subpass.pInputAttachments[j].attachment, i);
} }
} }
...@@ -100,10 +127,10 @@ RenderPass::RenderPass(const VkRenderPassCreateInfo* pCreateInfo, void* mem) : ...@@ -100,10 +127,10 @@ RenderPass::RenderPass(const VkRenderPassCreateInfo* pCreateInfo, void* mem) :
for (auto j = 0u; j < subpasses[i].colorAttachmentCount; j++) for (auto j = 0u; j < subpasses[i].colorAttachmentCount; j++)
{ {
if (subpass.pColorAttachments[j].attachment != VK_ATTACHMENT_UNUSED) if (subpass.pColorAttachments[j].attachment != VK_ATTACHMENT_UNUSED)
MarkFirstUse(attachmentFirstUse[subpass.pColorAttachments[j].attachment], i); MarkFirstUse(subpass.pColorAttachments[j].attachment, i);
if (subpass.pResolveAttachments && if (subpass.pResolveAttachments &&
subpass.pResolveAttachments[j].attachment != VK_ATTACHMENT_UNUSED) subpass.pResolveAttachments[j].attachment != VK_ATTACHMENT_UNUSED)
MarkFirstUse(attachmentFirstUse[subpass.pResolveAttachments[j].attachment], i); MarkFirstUse(subpass.pResolveAttachments[j].attachment, i);
} }
} }
...@@ -115,7 +142,7 @@ RenderPass::RenderPass(const VkRenderPassCreateInfo* pCreateInfo, void* mem) : ...@@ -115,7 +142,7 @@ RenderPass::RenderPass(const VkRenderPassCreateInfo* pCreateInfo, void* mem) :
hostMemory += sizeof(VkAttachmentReference); hostMemory += sizeof(VkAttachmentReference);
if (subpass.pDepthStencilAttachment->attachment != VK_ATTACHMENT_UNUSED) if (subpass.pDepthStencilAttachment->attachment != VK_ATTACHMENT_UNUSED)
MarkFirstUse(attachmentFirstUse[subpass.pDepthStencilAttachment->attachment], i); MarkFirstUse(subpass.pDepthStencilAttachment->attachment, i);
} }
if(subpass.preserveAttachmentCount > 0) if(subpass.preserveAttachmentCount > 0)
...@@ -129,7 +156,7 @@ RenderPass::RenderPass(const VkRenderPassCreateInfo* pCreateInfo, void* mem) : ...@@ -129,7 +156,7 @@ RenderPass::RenderPass(const VkRenderPassCreateInfo* pCreateInfo, void* mem) :
for (auto j = 0u; j < subpasses[i].preserveAttachmentCount; j++) for (auto j = 0u; j < subpasses[i].preserveAttachmentCount; j++)
{ {
if (subpass.pPreserveAttachments[j] != VK_ATTACHMENT_UNUSED) if (subpass.pPreserveAttachments[j] != VK_ATTACHMENT_UNUSED)
MarkFirstUse(attachmentFirstUse[subpass.pPreserveAttachments[j]], i); MarkFirstUse(subpass.pPreserveAttachments[j], i);
} }
} }
} }
...@@ -150,7 +177,8 @@ void RenderPass::destroy(const VkAllocationCallbacks* pAllocator) ...@@ -150,7 +177,8 @@ void RenderPass::destroy(const VkAllocationCallbacks* pAllocator)
size_t RenderPass::ComputeRequiredAllocationSize(const VkRenderPassCreateInfo* pCreateInfo) size_t RenderPass::ComputeRequiredAllocationSize(const VkRenderPassCreateInfo* pCreateInfo)
{ {
size_t attachmentSize = pCreateInfo->attachmentCount * sizeof(VkAttachmentDescription) size_t attachmentSize = pCreateInfo->attachmentCount * sizeof(VkAttachmentDescription)
+ pCreateInfo->attachmentCount * sizeof(int); + pCreateInfo->attachmentCount * sizeof(int) // first use
+ pCreateInfo->attachmentCount * sizeof(uint32_t); // union of subpass view masks, per attachment
size_t subpassesSize = 0; size_t subpassesSize = 0;
for(uint32_t i = 0; i < pCreateInfo->subpassCount; ++i) for(uint32_t i = 0; i < pCreateInfo->subpassCount; ++i)
{ {
...@@ -166,7 +194,8 @@ size_t RenderPass::ComputeRequiredAllocationSize(const VkRenderPassCreateInfo* p ...@@ -166,7 +194,8 @@ size_t RenderPass::ComputeRequiredAllocationSize(const VkRenderPassCreateInfo* p
} }
subpassesSize += sizeof(VkSubpassDescription) + subpassesSize += sizeof(VkSubpassDescription) +
sizeof(VkAttachmentReference) * nbAttachments + sizeof(VkAttachmentReference) * nbAttachments +
sizeof(uint32_t) * subpass.preserveAttachmentCount; sizeof(uint32_t) * subpass.preserveAttachmentCount +
sizeof(uint32_t); // view mask
} }
size_t dependenciesSize = pCreateInfo->dependencyCount * sizeof(VkSubpassDependency); size_t dependenciesSize = pCreateInfo->dependencyCount * sizeof(VkSubpassDependency);
...@@ -195,4 +224,16 @@ void RenderPass::end() ...@@ -195,4 +224,16 @@ void RenderPass::end()
currentSubpass = 0; currentSubpass = 0;
} }
void RenderPass::MarkFirstUse(int attachment, int subpass)
{
// FIXME: we may not actually need to track attachmentFirstUse if we're going to eagerly
// clear attachments at the start of the renderpass; can use attachmentViewMasks always instead.
if (attachmentFirstUse[attachment] == -1)
attachmentFirstUse[attachment] = subpass;
if (isMultiView())
attachmentViewMasks[attachment] |= viewMasks[subpass];
}
} // namespace vk } // namespace vk
\ No newline at end of file
...@@ -76,6 +76,21 @@ public: ...@@ -76,6 +76,21 @@ public:
return attachmentFirstUse[i] >= 0; return attachmentFirstUse[i] >= 0;
} }
uint32_t getViewMask() const
{
return viewMasks ? viewMasks[currentSubpass] : 1;
}
bool isMultiView() const
{
return viewMasks != nullptr;
}
uint32_t getAttachmentViewMask(uint32_t i) const
{
return attachmentViewMasks[i];
}
private: private:
uint32_t attachmentCount = 0; uint32_t attachmentCount = 0;
VkAttachmentDescription* attachments = nullptr; VkAttachmentDescription* attachments = nullptr;
...@@ -85,6 +100,10 @@ private: ...@@ -85,6 +100,10 @@ private:
VkSubpassDependency* dependencies = nullptr; VkSubpassDependency* dependencies = nullptr;
uint32_t currentSubpass = 0; uint32_t currentSubpass = 0;
int* attachmentFirstUse = nullptr; int* attachmentFirstUse = nullptr;
uint32_t* viewMasks = nullptr;
uint32_t* attachmentViewMasks = nullptr;
void MarkFirstUse(int attachment, int subpass);
}; };
static inline RenderPass* Cast(VkRenderPass object) static inline RenderPass* Cast(VkRenderPass object)
......
...@@ -511,8 +511,7 @@ VKAPI_ATTR VkResult VKAPI_CALL vkCreateDevice(VkPhysicalDevice physicalDevice, c ...@@ -511,8 +511,7 @@ VKAPI_ATTR VkResult VKAPI_CALL vkCreateDevice(VkPhysicalDevice physicalDevice, c
{ {
const VkPhysicalDeviceMultiviewFeatures* multiviewFeatures = reinterpret_cast<const VkPhysicalDeviceMultiviewFeatures*>(extensionCreateInfo); const VkPhysicalDeviceMultiviewFeatures* multiviewFeatures = reinterpret_cast<const VkPhysicalDeviceMultiviewFeatures*>(extensionCreateInfo);
if (multiviewFeatures->multiview || if (multiviewFeatures->multiviewGeometryShader ||
multiviewFeatures->multiviewGeometryShader ||
multiviewFeatures->multiviewTessellationShader) multiviewFeatures->multiviewTessellationShader)
{ {
return VK_ERROR_FEATURE_NOT_PRESENT; return VK_ERROR_FEATURE_NOT_PRESENT;
......
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