aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/gpu/vk
diff options
context:
space:
mode:
authorGravatar Chris Dalton <csmartdalton@google.com>2017-05-31 12:51:23 -0600
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2017-05-31 20:40:20 +0000
commit1d6163577c8a4f1372208e2c9e03b1a69906d385 (patch)
treefdacaa2e860d507bafca16cef0cb6e6e7861a1aa /src/gpu/vk
parentfa6d865215b48fac4ee24c120736e500d418f641 (diff)
Add support for instanced draws
Adds an instance buffer to GrMesh and instance attribs to GrPrimitiveProcessor. Implements support in GL and Vulkan. Adds unit tests for instanced rendering with GrMesh. Bug: skia: Change-Id: If1a9920feb9366f346b8c37cf914713c49129b3a Reviewed-on: https://skia-review.googlesource.com/16200 Reviewed-by: Brian Salomon <bsalomon@google.com> Commit-Queue: Chris Dalton <csmartdalton@google.com>
Diffstat (limited to 'src/gpu/vk')
-rw-r--r--src/gpu/vk/GrVkCaps.cpp2
-rw-r--r--src/gpu/vk/GrVkCommandBuffer.cpp6
-rw-r--r--src/gpu/vk/GrVkCommandBuffer.h26
-rw-r--r--src/gpu/vk/GrVkCopyManager.cpp2
-rw-r--r--src/gpu/vk/GrVkGpuCommandBuffer.cpp69
-rw-r--r--src/gpu/vk/GrVkGpuCommandBuffer.h30
-rw-r--r--src/gpu/vk/GrVkPipeline.cpp51
7 files changed, 119 insertions, 67 deletions
diff --git a/src/gpu/vk/GrVkCaps.cpp b/src/gpu/vk/GrVkCaps.cpp
index 16b46096e4..e8a397847c 100644
--- a/src/gpu/vk/GrVkCaps.cpp
+++ b/src/gpu/vk/GrVkCaps.cpp
@@ -32,6 +32,7 @@ GrVkCaps::GrVkCaps(const GrContextOptions& contextOptions, const GrVkInterface*
fReuseScratchTextures = true; //TODO: figure this out
fGpuTracingSupport = false; //TODO: figure this out
fOversizedStencilSupport = false; //TODO: figure this out
+ fInstanceAttribSupport = true;
fUseDrawInsteadOfClear = false;
fFenceSyncSupport = true; // always available in Vulkan
@@ -247,6 +248,7 @@ void GrVkCaps::initShaderCaps(const VkPhysicalDeviceProperties& properties, uint
}
shaderCaps->fIntegerSupport = true;
+ shaderCaps->fVertexIDSupport = true;
// Assume the minimum precisions mandated by the SPIR-V spec.
shaderCaps->fShaderPrecisionVaries = true;
diff --git a/src/gpu/vk/GrVkCommandBuffer.cpp b/src/gpu/vk/GrVkCommandBuffer.cpp
index cc219f5ec8..ea0a02cb34 100644
--- a/src/gpu/vk/GrVkCommandBuffer.cpp
+++ b/src/gpu/vk/GrVkCommandBuffer.cpp
@@ -18,10 +18,10 @@
#include "SkRect.h"
void GrVkCommandBuffer::invalidateState() {
- fBoundVertexBuffer = VK_NULL_HANDLE;
- fBoundVertexBufferIsValid = false;
+ for (auto& boundInputBuffer : fBoundInputBuffers) {
+ boundInputBuffer = VK_NULL_HANDLE;
+ }
fBoundIndexBuffer = VK_NULL_HANDLE;
- fBoundIndexBufferIsValid = false;
memset(&fCachedViewport, 0, sizeof(VkViewport));
fCachedViewport.width = - 1.0f; // Viewport must have a width greater than 0
diff --git a/src/gpu/vk/GrVkCommandBuffer.h b/src/gpu/vk/GrVkCommandBuffer.h
index 7c54877ec2..1f3c4a50e5 100644
--- a/src/gpu/vk/GrVkCommandBuffer.h
+++ b/src/gpu/vk/GrVkCommandBuffer.h
@@ -40,33 +40,36 @@ public:
BarrierType barrierType,
void* barrier) const;
- void bindVertexBuffer(GrVkGpu* gpu, const GrVkVertexBuffer* vbuffer) {
+ static constexpr uint32_t kMaxInputBuffers = 2;
+
+ void bindInputBuffer(GrVkGpu* gpu, uint32_t binding, const GrVkVertexBuffer* vbuffer) {
VkBuffer vkBuffer = vbuffer->buffer();
+ SkASSERT(VK_NULL_HANDLE != vkBuffer);
+ SkASSERT(binding < kMaxInputBuffers);
// TODO: once vbuffer->offset() no longer always returns 0, we will need to track the offset
// to know if we can skip binding or not.
- if (!fBoundVertexBufferIsValid || vkBuffer != fBoundVertexBuffer) {
+ if (vkBuffer != fBoundInputBuffers[binding]) {
VkDeviceSize offset = vbuffer->offset();
GR_VK_CALL(gpu->vkInterface(), CmdBindVertexBuffers(fCmdBuffer,
- 0,
+ binding,
1,
&vkBuffer,
&offset));
- fBoundVertexBufferIsValid = true;
- fBoundVertexBuffer = vkBuffer;
+ fBoundInputBuffers[binding] = vkBuffer;
addResource(vbuffer->resource());
}
}
void bindIndexBuffer(GrVkGpu* gpu, const GrVkIndexBuffer* ibuffer) {
VkBuffer vkBuffer = ibuffer->buffer();
+ SkASSERT(VK_NULL_HANDLE != vkBuffer);
// TODO: once ibuffer->offset() no longer always returns 0, we will need to track the offset
// to know if we can skip binding or not.
- if (!fBoundIndexBufferIsValid || vkBuffer != fBoundIndexBuffer) {
+ if (vkBuffer != fBoundIndexBuffer) {
GR_VK_CALL(gpu->vkInterface(), CmdBindIndexBuffer(fCmdBuffer,
vkBuffer,
ibuffer->offset(),
VK_INDEX_TYPE_UINT16));
- fBoundIndexBufferIsValid = true;
fBoundIndexBuffer = vkBuffer;
addResource(ibuffer->resource());
}
@@ -146,8 +149,6 @@ protected:
: fIsActive(false)
, fActiveRenderPass(rp)
, fCmdBuffer(cmdBuffer)
- , fBoundVertexBufferIsValid(false)
- , fBoundIndexBufferIsValid(false)
, fNumResets(0) {
fTrackedResources.setReserve(kInitialTrackedResourcesCount);
fTrackedRecycledResources.setReserve(kInitialTrackedResourcesCount);
@@ -177,11 +178,8 @@ private:
virtual void onReset(GrVkGpu* gpu) {}
- VkBuffer fBoundVertexBuffer;
- bool fBoundVertexBufferIsValid;
-
- VkBuffer fBoundIndexBuffer;
- bool fBoundIndexBufferIsValid;
+ VkBuffer fBoundInputBuffers[kMaxInputBuffers];
+ VkBuffer fBoundIndexBuffer;
// When resetting the command buffer, we remove the tracked resources from their arrays, and
// we prefer to not free all the memory every time so usually we just rewind. However, to avoid
diff --git a/src/gpu/vk/GrVkCopyManager.cpp b/src/gpu/vk/GrVkCopyManager.cpp
index 5301deae49..cd5dba4b18 100644
--- a/src/gpu/vk/GrVkCopyManager.cpp
+++ b/src/gpu/vk/GrVkCopyManager.cpp
@@ -377,7 +377,7 @@ bool GrVkCopyManager::copySurfaceAsDraw(GrVkGpu* gpu,
scissor.offset.y = 0;
cmdBuffer->setScissor(gpu, 0, 1, &scissor);
- cmdBuffer->bindVertexBuffer(gpu, fVertexBuffer.get());
+ cmdBuffer->bindInputBuffer(gpu, 0, fVertexBuffer.get());
cmdBuffer->draw(gpu, 4, 1, 0, 0);
cmdBuffer->endRenderPass(gpu);
diff --git a/src/gpu/vk/GrVkGpuCommandBuffer.cpp b/src/gpu/vk/GrVkGpuCommandBuffer.cpp
index 2e9373b130..6f39cfa545 100644
--- a/src/gpu/vk/GrVkGpuCommandBuffer.cpp
+++ b/src/gpu/vk/GrVkGpuCommandBuffer.cpp
@@ -426,17 +426,35 @@ void GrVkGpuCommandBuffer::inlineUpload(GrOpFlushState* state, GrDrawOp::Deferre
void GrVkGpuCommandBuffer::bindGeometry(const GrPrimitiveProcessor& primProc,
const GrBuffer* indexBuffer,
- const GrBuffer* vertexBuffer) {
+ const GrBuffer* vertexBuffer,
+ const GrBuffer* instanceBuffer) {
GrVkSecondaryCommandBuffer* currCmdBuf = fCommandBufferInfos[fCurrentCmdInfo].currentCmdBuf();
// There is no need to put any memory barriers to make sure host writes have finished here.
// When a command buffer is submitted to a queue, there is an implicit memory barrier that
// occurs for all host writes. Additionally, BufferMemoryBarriers are not allowed inside of
// an active RenderPass.
- SkASSERT(vertexBuffer);
- SkASSERT(!vertexBuffer->isCPUBacked());
- SkASSERT(!vertexBuffer->isMapped());
- currCmdBuf->bindVertexBuffer(fGpu, static_cast<const GrVkVertexBuffer*>(vertexBuffer));
+ // Here our vertex and instance inputs need to match the same 0-based bindings they were
+ // assigned in GrVkPipeline. That is, vertex first (if any) followed by instance.
+ uint32_t binding = 0;
+
+ if (primProc.hasVertexAttribs()) {
+ SkASSERT(vertexBuffer);
+ SkASSERT(!vertexBuffer->isCPUBacked());
+ SkASSERT(!vertexBuffer->isMapped());
+
+ currCmdBuf->bindInputBuffer(fGpu, binding++,
+ static_cast<const GrVkVertexBuffer*>(vertexBuffer));
+ }
+
+ if (primProc.hasInstanceAttribs()) {
+ SkASSERT(instanceBuffer);
+ SkASSERT(!instanceBuffer->isCPUBacked());
+ SkASSERT(!instanceBuffer->isMapped());
+
+ currCmdBuf->bindInputBuffer(fGpu, binding++,
+ static_cast<const GrVkVertexBuffer*>(instanceBuffer));
+ }
if (indexBuffer) {
SkASSERT(indexBuffer);
@@ -575,29 +593,34 @@ void GrVkGpuCommandBuffer::onDraw(const GrPipeline& pipeline,
pipelineState->freeTempResources(fGpu);
}
-void GrVkGpuCommandBuffer::sendMeshToGpu(const GrPrimitiveProcessor& primProc,
- GrPrimitiveType,
- const GrBuffer* vertexBuffer,
- int vertexCount,
- int baseVertex) {
+void GrVkGpuCommandBuffer::sendInstancedMeshToGpu(const GrPrimitiveProcessor& primProc,
+ GrPrimitiveType,
+ const GrBuffer* vertexBuffer,
+ int vertexCount,
+ int baseVertex,
+ const GrBuffer* instanceBuffer,
+ int instanceCount,
+ int baseInstance) {
CommandBufferInfo& cbInfo = fCommandBufferInfos[fCurrentCmdInfo];
- this->bindGeometry(primProc, nullptr, vertexBuffer);
- cbInfo.currentCmdBuf()->draw(fGpu, vertexCount, 1, baseVertex, 0);
+ this->bindGeometry(primProc, nullptr, vertexBuffer, instanceBuffer);
+ cbInfo.currentCmdBuf()->draw(fGpu, vertexCount, instanceCount, baseVertex, baseInstance);
fGpu->stats()->incNumDraws();
}
-void GrVkGpuCommandBuffer::sendIndexedMeshToGpu(const GrPrimitiveProcessor& primProc,
- GrPrimitiveType,
- const GrBuffer* indexBuffer,
- int indexCount,
- int baseIndex,
- uint16_t /*minIndexValue*/,
- uint16_t /*maxIndexValue*/,
- const GrBuffer* vertexBuffer,
- int baseVertex) {
+void GrVkGpuCommandBuffer::sendIndexedInstancedMeshToGpu(const GrPrimitiveProcessor& primProc,
+ GrPrimitiveType,
+ const GrBuffer* indexBuffer,
+ int indexCount,
+ int baseIndex,
+ const GrBuffer* vertexBuffer,
+ int baseVertex,
+ const GrBuffer* instanceBuffer,
+ int instanceCount,
+ int baseInstance) {
CommandBufferInfo& cbInfo = fCommandBufferInfos[fCurrentCmdInfo];
- this->bindGeometry(primProc, indexBuffer, vertexBuffer);
- cbInfo.currentCmdBuf()->drawIndexed(fGpu, indexCount, 1, baseIndex, baseVertex, 0);
+ this->bindGeometry(primProc, indexBuffer, vertexBuffer, instanceBuffer);
+ cbInfo.currentCmdBuf()->drawIndexed(fGpu, indexCount, instanceCount,
+ baseIndex, baseVertex, baseInstance);
fGpu->stats()->incNumDraws();
}
diff --git a/src/gpu/vk/GrVkGpuCommandBuffer.h b/src/gpu/vk/GrVkGpuCommandBuffer.h
index b1e96a3e11..6836fac0ca 100644
--- a/src/gpu/vk/GrVkGpuCommandBuffer.h
+++ b/src/gpu/vk/GrVkGpuCommandBuffer.h
@@ -48,7 +48,8 @@ private:
// Bind vertex and index buffers
void bindGeometry(const GrPrimitiveProcessor&,
const GrBuffer* indexBuffer,
- const GrBuffer* vertexBuffer);
+ const GrBuffer* vertexBuffer,
+ const GrBuffer* instanceBuffer);
sk_sp<GrVkPipelineState> prepareDrawState(const GrPipeline&,
const GrPrimitiveProcessor&,
@@ -62,13 +63,30 @@ private:
// GrMesh::SendToGpuImpl methods. These issue the actual Vulkan draw commands.
// Marked final as a hint to the compiler to not use virtual dispatch.
- void sendMeshToGpu(const GrPrimitiveProcessor&, GrPrimitiveType,
- const GrBuffer* vertexBuffer, int vertexCount, int baseVertex) final;
+ void sendMeshToGpu(const GrPrimitiveProcessor& primProc, GrPrimitiveType primType,
+ const GrBuffer* vertexBuffer, int vertexCount, int baseVertex) final {
+ this->sendInstancedMeshToGpu(primProc, primType, vertexBuffer, vertexCount, baseVertex,
+ nullptr, 1, 0);
+ }
- void sendIndexedMeshToGpu(const GrPrimitiveProcessor&, GrPrimitiveType,
+ void sendIndexedMeshToGpu(const GrPrimitiveProcessor& primProc, GrPrimitiveType primType,
const GrBuffer* indexBuffer, int indexCount, int baseIndex,
- uint16_t minIndexValue, uint16_t maxIndexValue,
- const GrBuffer* vertexBuffer, int baseVertex) final;
+ uint16_t /*minIndexValue*/, uint16_t /*maxIndexValue*/,
+ const GrBuffer* vertexBuffer, int baseVertex) final {
+ this->sendIndexedInstancedMeshToGpu(primProc, primType, indexBuffer, indexCount, baseIndex,
+ vertexBuffer, baseVertex, nullptr, 1, 0);
+ }
+
+ void sendInstancedMeshToGpu(const GrPrimitiveProcessor&, GrPrimitiveType,
+ const GrBuffer* vertexBuffer, int vertexCount, int baseVertex,
+ const GrBuffer* instanceBuffer, int instanceCount,
+ int baseInstance) final;
+
+ void sendIndexedInstancedMeshToGpu(const GrPrimitiveProcessor&, GrPrimitiveType,
+ const GrBuffer* indexBuffer, int indexCount, int baseIndex,
+ const GrBuffer* vertexBuffer, int baseVertex,
+ const GrBuffer* instanceBuffer, int instanceCount,
+ int baseInstance) final;
void onClear(GrRenderTarget*, const GrFixedClip&, GrColor color) override;
diff --git a/src/gpu/vk/GrVkPipeline.cpp b/src/gpu/vk/GrVkPipeline.cpp
index 47acb94266..2732c6fc80 100644
--- a/src/gpu/vk/GrVkPipeline.cpp
+++ b/src/gpu/vk/GrVkPipeline.cpp
@@ -46,30 +46,41 @@ static inline VkFormat attrib_type_to_vkformat(GrVertexAttribType type) {
}
static void setup_vertex_input_state(const GrPrimitiveProcessor& primProc,
- VkPipelineVertexInputStateCreateInfo* vertexInputInfo,
- VkVertexInputBindingDescription* bindingDesc,
- int maxBindingDescCount,
- VkVertexInputAttributeDescription* attributeDesc) {
- // for now we have only one vertex buffer and one binding
- memset(bindingDesc, 0, sizeof(VkVertexInputBindingDescription));
- bindingDesc->binding = 0;
- bindingDesc->stride = (uint32_t)primProc.getVertexStride();
- bindingDesc->inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
+ VkPipelineVertexInputStateCreateInfo* vertexInputInfo,
+ SkSTArray<2, VkVertexInputBindingDescription, true>* bindingDescs,
+ VkVertexInputAttributeDescription* attributeDesc) {
+ uint32_t vertexBinding, instanceBinding;
+
+ if (primProc.hasVertexAttribs()) {
+ vertexBinding = bindingDescs->count();
+ bindingDescs->push_back() = {
+ vertexBinding,
+ (uint32_t) primProc.getVertexStride(),
+ VK_VERTEX_INPUT_RATE_VERTEX
+ };
+ }
+
+ if (primProc.hasInstanceAttribs()) {
+ instanceBinding = bindingDescs->count();
+ bindingDescs->push_back() = {
+ instanceBinding,
+ (uint32_t) primProc.getInstanceStride(),
+ VK_VERTEX_INPUT_RATE_INSTANCE
+ };
+ }
// setup attribute descriptions
int vaCount = primProc.numAttribs();
if (vaCount > 0) {
- size_t offset = 0;
for (int attribIndex = 0; attribIndex < vaCount; attribIndex++) {
+ using InputRate = GrPrimitiveProcessor::Attribute::InputRate;
const GrGeometryProcessor::Attribute& attrib = primProc.getAttrib(attribIndex);
- GrVertexAttribType attribType = attrib.fType;
-
VkVertexInputAttributeDescription& vkAttrib = attributeDesc[attribIndex];
vkAttrib.location = attribIndex; // for now assume location = attribIndex
- vkAttrib.binding = 0; // for now only one vertex buffer & binding
- vkAttrib.format = attrib_type_to_vkformat(attribType);
- vkAttrib.offset = static_cast<uint32_t>(offset);
- offset += attrib.fOffset;
+ vkAttrib.binding = InputRate::kPerInstance == attrib.fInputRate ? instanceBinding
+ : vertexBinding;
+ vkAttrib.format = attrib_type_to_vkformat(attrib.fType);
+ vkAttrib.offset = attrib.fOffsetInRecord;
}
}
@@ -77,8 +88,8 @@ static void setup_vertex_input_state(const GrPrimitiveProcessor& primProc,
vertexInputInfo->sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertexInputInfo->pNext = nullptr;
vertexInputInfo->flags = 0;
- vertexInputInfo->vertexBindingDescriptionCount = 1;
- vertexInputInfo->pVertexBindingDescriptions = bindingDesc;
+ vertexInputInfo->vertexBindingDescriptionCount = bindingDescs->count();
+ vertexInputInfo->pVertexBindingDescriptions = bindingDescs->begin();
vertexInputInfo->vertexAttributeDescriptionCount = vaCount;
vertexInputInfo->pVertexAttributeDescriptions = attributeDesc;
}
@@ -402,11 +413,11 @@ GrVkPipeline* GrVkPipeline::Create(GrVkGpu* gpu, const GrPipeline& pipeline,
VkPipelineLayout layout,
VkPipelineCache cache) {
VkPipelineVertexInputStateCreateInfo vertexInputInfo;
- VkVertexInputBindingDescription bindingDesc;
+ SkSTArray<2, VkVertexInputBindingDescription, true> bindingDescs;
SkSTArray<16, VkVertexInputAttributeDescription> attributeDesc;
SkASSERT(primProc.numAttribs() <= gpu->vkCaps().maxVertexAttributes());
VkVertexInputAttributeDescription* pAttribs = attributeDesc.push_back_n(primProc.numAttribs());
- setup_vertex_input_state(primProc, &vertexInputInfo, &bindingDesc, 1, pAttribs);
+ setup_vertex_input_state(primProc, &vertexInputInfo, &bindingDescs, pAttribs);
VkPipelineInputAssemblyStateCreateInfo inputAssemblyInfo;
setup_input_assembly_state(primitiveType, &inputAssemblyInfo);