Updated (multi) indirect draw example

This commit is contained in:
saschawillems 2016-08-01 21:43:47 +02:00
parent be68bc0a5a
commit 444c4b9c24
23 changed files with 1565 additions and 214 deletions

101
indirectdraw/README.md Normal file
View file

@ -0,0 +1,101 @@
# Indirect drawing
<img src="../screenshots/indirectdraw.jpg" height="256px">
## Synopsis
Issue multiple instanced draws for different meshes in one single draw call using indirect draw commands.
## Requirements
The example requires a GPU that supports the [`multiDrawIndirect`](http://vulkan.gpuinfo.org/listreports.php?feature=multiDrawIndirect) feature. If this feature is not available, the `drawCount` parameter for the `vkCmdDrawIndexedIndirect` call must be 1.
When issuing many draw counts also make sure to stay within the limitations of [`maxDrawIndirectCount`](http://vulkan.gpuinfo.org/listreports.php?limit=maxDrawIndirectCount).
## Description
This example demonstrates the use of indirect draw commands. In addition to draw functions like [`vkCmdDraw`](https://www.khronos.org/registry/vulkan/specs/1.0/man/html/vkCmdDraw.html) and [`vkCmdDrawIndexed`](https://www.khronos.org/registry/vulkan/specs/1.0/man/html/vkCmdDrawIndexed.html), where the parameters that specify what is drawn are passed directly to the function ("direct drawing"), there also exist indirect drawing commands.
[`vkCmdDrawIndirect`](https://www.khronos.org/registry/vulkan/specs/1.0/man/html/vkCmdDrawIndirect.html) and [`vkCmdDrawIndexedIndirect`](https://www.khronos.org/registry/vulkan/specs/1.0/man/html/vkCmdDrawIndexedIndirect.html) take the draw commands from a buffer object that contains descriptions on the draw commands to be issued, including instance and index counts, vertex offsets, etc. This also allows to draw multiple geometries with a single draw command as long as they're backed up by the same vertex (and index) buffer.
This adds several new possibilities of generating (and updating) actual draw commands, as that buffer can be generated and updated offline with no need to actually update the command buffers that contain the actual drawing functions.
Using indirect drawing you can generate the draw commands offline ahead of time on the CPU and even update them using shaders (as they're stored in a device local buffer). This adds lots of new possibilites to update draw commands without the CPU being involved, including GPU-based culling.
The example generates a single indirect buffer that contains draw commands for 12 different plants at random position, scale and rotation also using instancing to render the objects multiple times. The whole foliage (and trees) seen in the screen are drawn using only one draw call.
The different plant meshes are loaded from a single file and stored inside a single index and vertex buffer, index offsets are stored inside the indirect draw commands.
For details on the use of instancing (and instanced vertex attributes), see the [instancing example](../instancing)
## Points of interest
### Preparing the indirect draw
The example generates the indirect drawing buffer right at the start. First step is to generate the data for the indirect draws. Vulkan has a dedicated struct for this called `VkDrawIndexedIndirectCommand` and the example uses a `std::vector<VkDrawIndexedIndirectCommand>` to store these before uploading them to the GPU:
```cpp
void prepareIndirectData()
{
...
uint32_t m = 0;
for (auto& meshDescriptor : meshes.plants.meshDescriptors)
{
VkDrawIndexedIndirectCommand indirectCmd{};
indirectCmd.instanceCount = OBJECT_INSTANCE_COUNT;
indirectCmd.firstInstance = m * OBJECT_INSTANCE_COUNT;
indirectCmd.firstIndex = meshDescriptor.indexBase;
indirectCmd.indexCount = meshDescriptor.indexCount;
indirectCommands.push_back(indirectCmd);
m++;
}
...
}
```
The meshDescriptor is generated by the mesh loader and contains the index base and count of that mesh inside the global index buffer containing all plant meshes for the scenery.
The indirect draw command for the second plant mesh looks like this:
```cpp
indirectCmd.indexCount = 1668;
indirectCmd.instanceCount = 2048;
indirectCmd.firstIndex = 960;
indirectCmd.vertexOffset = 0;
indirectCmd.firstInstance = 2048;
```
Which will result in 2048 instances of the index data starting at index 960 (using 1668 indices) being drawn, the first instance is also important as the shader is using it for the instanced attributes of that object (position, rotation, scale).
Once we have filled that vector we need to create the buffer that the GPU uses to read the indirect commands from:
```cpp
VK_CHECK_RESULT(vulkanDevice->createBuffer(
VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
&indirectCommandsBuffer,
stagingBuffer.size));
vulkanDevice->copyBuffer(&stagingBuffer, &indirectCommandsBuffer, queue);
```
To use a buffer for indirect draw commands you need to specify the ```VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT``` usage flag at creation time. As the buffer is never again changed on the host side we stage it to the GPU to maximize performance.
### Rendering
Issuing the actual indirect draw command is pretty straightforward:
```cpp
void buildCommandBuffers()
{
...
for (int32_t i = 0; i < drawCmdBuffers.size(); ++i)
{
vkCmdDrawIndexedIndirect(drawCmdBuffers[i], indirectCommandsBuffer.buffer, 0, indirectDrawCount, sizeof(VkDrawIndexedIndirectCommand));
}
}
```
We just pass the buffer handle to the buffer containing the indirect draw commands and the number of drawCounts.
The non-indirect, non-instanced equivalent of this would be:
```cpp
for (auto indirectCmd : indirectCommands)
{
for (uint32_t j = 0; j < indirectCmd.instanceCount; j++)
{
vkCmdDrawIndexed(drawCmdBuffers[i], indirectCmd.indexCount, 1, indirectCmd.firstIndex, 0, indirectCmd.firstInstance + j);
}
}
```
### Acknowledgements
- Plant and foliage models by [Hugues Muller](http://www.yughues-folio.com/)

View file

@ -6,13 +6,16 @@
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
*
* Summary:
* Use a device local buffer that stores draw commands for instanced rendering of different meshes.
* Use a device local buffer that stores draw commands for instanced rendering of different meshes stored
* in the same buffer.
*
* Indirect drawing offloads draw command generation and offers the ability to update them on the GPU
* without the CPU having to touch the buffer again, also reducing the number of drawcalls.
*
* The example shows how to setup and fill such a buffer on the CPU side, stages it to the device and
* shows how render it using only one draw command.
* shows how to render it using only one draw command.
*
* See readme.md for details
*
*/
@ -39,6 +42,8 @@
// Number of instances per object
#define OBJECT_INSTANCE_COUNT 2048
// Circular range of plant distribution
#define PLANT_RADIUS 25.0f
// Vertex layout for this example
std::vector<vkMeshLoader::VertexLayout> vertexLayout =
@ -59,18 +64,20 @@ public:
} vertices;
struct {
vkMeshLoader::MeshBuffer example;
vkMeshLoader::MeshBuffer plants;
vkMeshLoader::MeshBuffer ground;
vkMeshLoader::MeshBuffer skysphere;
} meshes;
struct {
vkTools::VulkanTexture colorMap;
vkTools::VulkanTexture ground;
} textures;
// Per-instance data block
struct InstanceData {
glm::vec3 pos;
glm::vec3 rot;
glm::vec3 color;
float scale;
uint32_t texIndex;
};
@ -84,7 +91,6 @@ public:
struct {
glm::mat4 projection;
glm::mat4 view;
float time = 0.0f;
} uboVS;
struct {
@ -92,13 +98,17 @@ public:
} uniformData;
struct {
VkPipeline solid;
VkPipeline plants;
VkPipeline ground;
VkPipeline skysphere;
} pipelines;
VkPipelineLayout pipelineLayout;
VkDescriptorSet descriptorSet;
VkDescriptorSetLayout descriptorSetLayout;
VkSampler samplerRepeat;
uint32_t objectCount = 0;
// Store the indirect draw commands containing index offsets and instance count per object
@ -106,19 +116,23 @@ public:
VulkanExample() : VulkanExampleBase(ENABLE_VALIDATION)
{
zoom = -12.0f;
rotationSpeed = 0.25f;
enableTextOverlay = true;
title = "Vulkan Example - Indirect rendering";
camera.type = Camera::CameraType::firstperson;
camera.setPerspective(60.0f, (float)width / (float)height, 0.1f, 512.0f);
camera.setRotation(glm::vec3(-12.0f, 159.0f, 0.0f));
camera.setTranslation(glm::vec3(0.4f, 1.25f, 0.0f));
camera.movementSpeed = 5.0f;
srand(time(NULL));
}
~VulkanExample()
{
vkDestroyPipeline(device, pipelines.solid, nullptr);
vkDestroyPipeline(device, pipelines.plants, nullptr);
vkDestroyPipeline(device, pipelines.ground, nullptr);
vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);
vkMeshLoader::freeMeshBufferResources(device, &meshes.example);
vkMeshLoader::freeMeshBufferResources(device, &meshes.plants);
textureLoader->destroyTexture(textures.colorMap);
instanceBuffer.destroy();
indirectCommandsBuffer.destroy();
@ -140,7 +154,7 @@ public:
VkCommandBufferBeginInfo cmdBufInfo = vkTools::initializers::commandBufferBeginInfo();
VkClearValue clearValues[2];
clearValues[0].color = { { 0.0f, 0.0f, 0.0f, 0.0f } };
clearValues[0].color = { { 0.18f, 0.27f, 0.5f, 0.0f } };
clearValues[1].depthStencil = { 1.0f, 0 };
VkRenderPassBeginInfo renderPassBeginInfo = vkTools::initializers::renderPassBeginInfo();
@ -165,38 +179,47 @@ public:
VkRect2D scissor = vkTools::initializers::rect2D(width, height, 0, 0);
vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor);
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, NULL);
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.solid);
VkDeviceSize offsets[1] = { 0 };
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, NULL);
// Plants
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.plants);
// Binding point 0 : Mesh vertex buffer
vkCmdBindVertexBuffers(drawCmdBuffers[i], VERTEX_BUFFER_BIND_ID, 1, &meshes.example.vertices.buf, offsets);
vkCmdBindVertexBuffers(drawCmdBuffers[i], VERTEX_BUFFER_BIND_ID, 1, &meshes.plants.vertices.buf, offsets);
// Binding point 1 : Instance data buffer
vkCmdBindVertexBuffers(drawCmdBuffers[i], INSTANCE_BUFFER_BIND_ID, 1, &instanceBuffer.buffer, offsets);
vkCmdBindIndexBuffer(drawCmdBuffers[i], meshes.example.indices.buf, 0, VK_INDEX_TYPE_UINT32);
vkCmdBindIndexBuffer(drawCmdBuffers[i], meshes.plants.indices.buf, 0, VK_INDEX_TYPE_UINT32);
// One draw call for an arbitrary number of ojects
// Index offsets and instance count are taken from the indirect buffer
vkCmdDrawIndexedIndirect(drawCmdBuffers[i], indirectCommandsBuffer.buffer, 0, indirectDrawCount, sizeof(VkDrawIndexedIndirectCommand));
// Ground
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.ground);
vkCmdBindVertexBuffers(drawCmdBuffers[i], VERTEX_BUFFER_BIND_ID, 1, &meshes.ground.vertices.buf, offsets);
vkCmdBindIndexBuffer(drawCmdBuffers[i], meshes.ground.indices.buf, 0, VK_INDEX_TYPE_UINT32);
vkCmdDrawIndexed(drawCmdBuffers[i], meshes.ground.indexCount, 1, 0, 0, 0);
// Skysphere
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.skysphere);
vkCmdBindVertexBuffers(drawCmdBuffers[i], VERTEX_BUFFER_BIND_ID, 1, &meshes.skysphere.vertices.buf, offsets);
vkCmdBindIndexBuffer(drawCmdBuffers[i], meshes.skysphere.indices.buf, 0, VK_INDEX_TYPE_UINT32);
vkCmdDrawIndexed(drawCmdBuffers[i], meshes.skysphere.indexCount, 1, 0, 0, 0);
vkCmdEndRenderPass(drawCmdBuffers[i]);
VK_CHECK_RESULT(vkEndCommandBuffer(drawCmdBuffers[i]));
}
}
void loadMeshes()
void loadAssets()
{
loadMesh(getAssetPath() + "models/basicmeshes.dae", &meshes.example, vertexLayout, 0.1f);
}
loadMesh(getAssetPath() + "models/plants.dae", &meshes.plants, vertexLayout, 0.0025f);
loadMesh(getAssetPath() + "models/plane_circle.dae", &meshes.ground, vertexLayout, PLANT_RADIUS + 1.0f);
loadMesh(getAssetPath() + "models/skysphere.dae", &meshes.skysphere, vertexLayout, 512.0f);
void loadTextures()
{
textureLoader->loadTextureArray(
getAssetPath() + "textures/texturearray_rocks_bc3.ktx",
VK_FORMAT_BC3_UNORM_BLOCK,
&textures.colorMap);
textureLoader->loadTextureArray(getAssetPath() + "textures/texturearray_plants_bc3.ktx", VK_FORMAT_BC3_UNORM_BLOCK, &textures.colorMap);
textureLoader->loadTexture(getAssetPath() + "textures/ground_dry_bc3.ktx", VK_FORMAT_BC3_UNORM_BLOCK, &textures.ground);
}
void setupVertexDescriptions()
@ -270,20 +293,15 @@ public:
vkTools::initializers::vertexInputAttributeDescription(
INSTANCE_BUFFER_BIND_ID, 5, VK_FORMAT_R32G32B32_SFLOAT, offsetof(InstanceData, rot))
);
// Location 6: Color
vertices.attributeDescriptions.push_back(
vkTools::initializers::vertexInputAttributeDescription(
INSTANCE_BUFFER_BIND_ID, 6, VK_FORMAT_R32G32B32_SFLOAT, offsetof(InstanceData, color))
);
// Location 6: Scale
vertices.attributeDescriptions.push_back(
vkTools::initializers::vertexInputAttributeDescription(
INSTANCE_BUFFER_BIND_ID, 7, VK_FORMAT_R32_SFLOAT, offsetof(InstanceData, scale))
INSTANCE_BUFFER_BIND_ID, 6, VK_FORMAT_R32_SFLOAT, offsetof(InstanceData, scale))
);
// Location 7: Texture array layer index
vertices.attributeDescriptions.push_back(
vkTools::initializers::vertexInputAttributeDescription(
INSTANCE_BUFFER_BIND_ID, 8, VK_FORMAT_R32_SINT, offsetof(InstanceData, texIndex))
INSTANCE_BUFFER_BIND_ID, 7, VK_FORMAT_R32_SINT, offsetof(InstanceData, texIndex))
);
vertices.inputState = vkTools::initializers::pipelineVertexInputStateCreateInfo();
@ -299,7 +317,7 @@ public:
std::vector<VkDescriptorPoolSize> poolSizes =
{
vkTools::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1),
vkTools::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1),
vkTools::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2),
};
VkDescriptorPoolCreateInfo descriptorPoolInfo =
@ -315,16 +333,21 @@ public:
{
std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings =
{
// Binding 0 : Vertex shader uniform buffer
// Binding 0: Vertex shader uniform buffer
vkTools::initializers::descriptorSetLayoutBinding(
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
VK_SHADER_STAGE_VERTEX_BIT,
0),
// Binding 1 : Fragment shader combined sampler
// Binding 1: Fragment shader combined sampler (plants texture array)
vkTools::initializers::descriptorSetLayoutBinding(
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
VK_SHADER_STAGE_FRAGMENT_BIT,
1),
// Binding 1: Fragment shader combined sampler (ground texture)
vkTools::initializers::descriptorSetLayoutBinding(
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
VK_SHADER_STAGE_FRAGMENT_BIT,
2),
};
VkDescriptorSetLayoutCreateInfo descriptorLayout =
@ -352,26 +375,26 @@ public:
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet));
VkDescriptorImageInfo texDescriptor =
vkTools::initializers::descriptorImageInfo(
textures.colorMap.sampler,
textures.colorMap.view,
VK_IMAGE_LAYOUT_GENERAL);
std::vector<VkWriteDescriptorSet> writeDescriptorSets =
{
// Binding 0 : Vertex shader uniform buffer
// Binding 0: Vertex shader uniform buffer
vkTools::initializers::writeDescriptorSet(
descriptorSet,
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
0,
&uniformData.scene.descriptor),
// Binding 1 : Color map
// Binding 1: Plants texture array combined
vkTools::initializers::writeDescriptorSet(
descriptorSet,
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
1,
&texDescriptor)
&textures.colorMap.descriptor),
// Binding 2: Ground texture combined
vkTools::initializers::writeDescriptorSet(
descriptorSet,
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
2,
&textures.ground.descriptor)
};
vkUpdateDescriptorSets(device, writeDescriptorSets.size(), writeDescriptorSets.data(), 0, NULL);
@ -388,7 +411,7 @@ public:
VkPipelineRasterizationStateCreateInfo rasterizationState =
vkTools::initializers::pipelineRasterizationStateCreateInfo(
VK_POLYGON_MODE_FILL,
VK_CULL_MODE_BACK_BIT,
VK_CULL_MODE_NONE,
VK_FRONT_FACE_CLOCKWISE,
0);
@ -426,19 +449,14 @@ public:
dynamicStateEnables.size(),
0);
// Instacing pipeline
// Load shaders
std::array<VkPipelineShaderStageCreateInfo, 2> shaderStages;
shaderStages[0] = loadShader(getAssetPath() + "shaders/indirectdraw/indirectdraw.vert.spv", VK_SHADER_STAGE_VERTEX_BIT);
shaderStages[1] = loadShader(getAssetPath() + "shaders/indirectdraw/indirectdraw.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
VkGraphicsPipelineCreateInfo pipelineCreateInfo =
vkTools::initializers::pipelineCreateInfo(
pipelineLayout,
renderPass,
0);
std::array<VkPipelineShaderStageCreateInfo, 2> shaderStages;
pipelineCreateInfo.pVertexInputState = &vertices.inputState;
pipelineCreateInfo.pInputAssemblyState = &inputAssemblyState;
pipelineCreateInfo.pRasterizationState = &rasterizationState;
@ -450,29 +468,43 @@ public:
pipelineCreateInfo.stageCount = shaderStages.size();
pipelineCreateInfo.pStages = shaderStages.data();
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.solid));
// Indirect (and instanced) pipeline for the plants
shaderStages[0] = loadShader(getAssetPath() + "shaders/indirectdraw/indirectdraw.vert.spv", VK_SHADER_STAGE_VERTEX_BIT);
shaderStages[1] = loadShader(getAssetPath() + "shaders/indirectdraw/indirectdraw.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.plants));
// Ground
shaderStages[0] = loadShader(getAssetPath() + "shaders/indirectdraw/ground.vert.spv", VK_SHADER_STAGE_VERTEX_BIT);
shaderStages[1] = loadShader(getAssetPath() + "shaders/indirectdraw/ground.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
rasterizationState.cullMode = VK_CULL_MODE_BACK_BIT;
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.ground));
// Skysphere
shaderStages[0] = loadShader(getAssetPath() + "shaders/indirectdraw/skysphere.vert.spv", VK_SHADER_STAGE_VERTEX_BIT);
shaderStages[1] = loadShader(getAssetPath() + "shaders/indirectdraw/skysphere.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
rasterizationState.cullMode = VK_CULL_MODE_FRONT_BIT;
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.skysphere));
}
// Prepare (and stage) a buffer containing the indirect draw commands
void prepareIndirectData()
{
VkDrawIndexedIndirectCommand dic{};
dic.firstInstance = 0;
dic.instanceCount = OBJECT_INSTANCE_COUNT;
indirectCommands.clear();
dic.firstIndex = 0;
dic.indexCount = 36;
indirectCommands.push_back(dic);
// Create on indirect command for each mesh in the scene
uint32_t m = 0;
for (auto& meshDescriptor : meshes.plants.meshDescriptors)
{
VkDrawIndexedIndirectCommand indirectCmd{};
indirectCmd.instanceCount = OBJECT_INSTANCE_COUNT;
indirectCmd.firstInstance = m * OBJECT_INSTANCE_COUNT;
indirectCmd.firstIndex = meshDescriptor.indexBase;
indirectCmd.indexCount = meshDescriptor.indexCount;
indirectCommands.push_back(indirectCmd);
dic.firstInstance += OBJECT_INSTANCE_COUNT;
dic.firstIndex = 36;
dic.indexCount = 2880;
indirectCommands.push_back(dic);
dic.firstInstance += OBJECT_INSTANCE_COUNT;
dic.firstIndex = 2916;
dic.indexCount = 186;
indirectCommands.push_back(dic);
m++;
}
indirectDrawCount = static_cast<uint32_t>(indirectCommands.size());
@ -496,19 +528,7 @@ public:
&indirectCommandsBuffer,
stagingBuffer.size));
// Copy to staging buffer
VkCommandBuffer copyCmd = VulkanExampleBase::createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
VkBufferCopy copyRegion = {};
copyRegion.size = indirectCommandsBuffer.size;
vkCmdCopyBuffer(
copyCmd,
stagingBuffer.buffer,
indirectCommandsBuffer.buffer,
1,
&copyRegion);
VulkanExampleBase::flushCommandBuffer(copyCmd, queue, true);
vulkanDevice->copyBuffer(&stagingBuffer, &indirectCommandsBuffer, queue);
stagingBuffer.destroy();
}
@ -529,14 +549,12 @@ public:
for (auto i = 0; i < objectCount; i++)
{
instanceData[i].rot = glm::vec3(M_PI * uniformDist(rndGenerator), M_PI * uniformDist(rndGenerator), M_PI * uniformDist(rndGenerator));
instanceData[i].rot = glm::vec3(0.0f, M_PI * uniformDist(rndGenerator), 0.0f);
float theta = 2 * M_PI * uniformDist(rndGenerator);
float phi = acos(1 - 2 * uniformDist(rndGenerator));
glm::vec3 pos;
instanceData[i].pos = glm::vec3(sin(phi) * cos(theta), sin(theta) * uniformDist(rndGenerator) / 1500.0f, cos(phi)) * 7.5f;
instanceData[i].color = glm::vec3(rnd(1.0f), rnd(1.0f), rnd(1.0f));
instanceData[i].pos = glm::vec3(sin(phi) * cos(theta), 0.0f, cos(phi)) * PLANT_RADIUS;
instanceData[i].scale = 1.0f + uniformDist(rndGenerator) * 2.0f;
instanceData[i].texIndex = rnd(textures.colorMap.layerCount);
instanceData[i].texIndex = i / OBJECT_INSTANCE_COUNT;
}
vk::Buffer stagingBuffer;
@ -553,19 +571,7 @@ public:
&instanceBuffer,
stagingBuffer.size));
// Copy to staging buffer
VkCommandBuffer copyCmd = VulkanExampleBase::createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
VkBufferCopy copyRegion = { };
copyRegion.size = instanceBuffer.size;
vkCmdCopyBuffer(
copyCmd,
stagingBuffer.buffer,
instanceBuffer.buffer,
1,
&copyRegion);
VulkanExampleBase::flushCommandBuffer(copyCmd, queue, true);
vulkanDevice->copyBuffer(&stagingBuffer, &instanceBuffer, queue);
stagingBuffer.destroy();
}
@ -587,16 +593,8 @@ public:
{
if (viewChanged)
{
uboVS.projection = glm::perspective(glm::radians(60.0f), (float)width / (float)height, 0.001f, 256.0f);
uboVS.view = glm::translate(glm::mat4(), cameraPos + glm::vec3(0.0f, 0.0f, zoom));
uboVS.view = glm::rotate(uboVS.view, glm::radians(rotation.x), glm::vec3(1.0f, 0.0f, 0.0f));
uboVS.view = glm::rotate(uboVS.view, glm::radians(rotation.y), glm::vec3(0.0f, 1.0f, 0.0f));
uboVS.view = glm::rotate(uboVS.view, glm::radians(rotation.z), glm::vec3(0.0f, 0.0f, 1.0f));
}
if (!paused)
{
uboVS.time += frameTimer * 0.05f;
uboVS.projection = camera.matrices.perspective;
uboVS.view = camera.matrices.view;
}
memcpy(uniformData.scene.mapped, &uboVS, sizeof(uboVS));
@ -619,8 +617,7 @@ public:
void prepare()
{
VulkanExampleBase::prepare();
loadTextures();
loadMeshes();
loadAssets();
prepareIndirectData();
prepareInstanceData();
setupVertexDescriptions();
@ -640,10 +637,6 @@ public:
return;
}
draw();
if (!paused)
{
updateUniformBuffer(false);
}
}
virtual void viewChanged()
@ -653,67 +646,8 @@ public:
virtual void getOverlayText(VulkanTextOverlay *textOverlay)
{
textOverlay->addText("Rendering " + std::to_string(objectCount) + " objects", 5.0f, 85.0f, VulkanTextOverlay::alignLeft);
textOverlay->addText(std::to_string(objectCount) + " objects", 5.0f, 85.0f, VulkanTextOverlay::alignLeft);
}
};
VulkanExample *vulkanExample;
#if defined(_WIN32)
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (vulkanExample != NULL)
{
vulkanExample->handleMessages(hWnd, uMsg, wParam, lParam);
}
return (DefWindowProc(hWnd, uMsg, wParam, lParam));
}
#elif defined(__linux__) && !defined(__ANDROID__)
static void handleEvent(const xcb_generic_event_t *event)
{
if (vulkanExample != NULL)
{
vulkanExample->handleEvent(event);
}
}
#endif
// Main entry point
#if defined(_WIN32)
// Windows entry point
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pCmdLine, int nCmdShow)
#elif defined(__ANDROID__)
// Android entry point
void android_main(android_app* state)
#elif defined(__linux__)
// Linux entry point
int main(const int argc, const char *argv[])
#endif
{
#if defined(__ANDROID__)
// Removing this may cause the compiler to omit the main entry point
// which would make the application crash at start
app_dummy();
#endif
vulkanExample = new VulkanExample();
#if defined(_WIN32)
vulkanExample->setupWindow(hInstance, WndProc);
#elif defined(__ANDROID__)
// Attach vulkan example to global android application state
state->userData = vulkanExample;
state->onAppCmd = VulkanExample::handleAppCommand;
state->onInputEvent = VulkanExample::handleAppInput;
vulkanExample->androidApp = state;
#elif defined(__linux__)
vulkanExample->setupWindow();
#endif
#if !defined(__ANDROID__)
vulkanExample->initSwapchain();
vulkanExample->prepare();
#endif
vulkanExample->renderLoop();
delete(vulkanExample);
#if !defined(__ANDROID__)
return 0;
#endif
}
VULKAN_EXAMPLE_MAIN()

View file

@ -22,8 +22,12 @@
<ClInclude Include="..\base\vulkantools.h" />
</ItemGroup>
<ItemGroup>
<None Include="..\data\shaders\indirectdraw\ground.frag" />
<None Include="..\data\shaders\indirectdraw\ground.vert" />
<None Include="..\data\shaders\indirectdraw\indirectdraw.frag" />
<None Include="..\data\shaders\indirectdraw\indirectdraw.vert" />
<None Include="..\data\shaders\indirectdraw\skysphere.frag" />
<None Include="..\data\shaders\indirectdraw\skysphere.vert" />
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{2BBDD10F-2C9D-4BEA-8C7B-1C510A2CE08B}</ProjectGuid>

View file

@ -49,5 +49,17 @@
<None Include="..\data\shaders\indirectdraw\indirectdraw.vert">
<Filter>Shaders</Filter>
</None>
<None Include="..\data\shaders\indirectdraw\ground.frag">
<Filter>Shaders</Filter>
</None>
<None Include="..\data\shaders\indirectdraw\ground.vert">
<Filter>Shaders</Filter>
</None>
<None Include="..\data\shaders\indirectdraw\skysphere.frag">
<Filter>Shaders</Filter>
</None>
<None Include="..\data\shaders\indirectdraw\skysphere.vert">
<Filter>Shaders</Filter>
</None>
</ItemGroup>
</Project>