Files
2026-06-14 19:09:18 +01:00

770 lines
28 KiB
C++

/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab: */
/*
* Copyright 2017-2020 Mark Callow.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @internal
* @class InstancedSampleBase
* @~English
*
* @brief Base for tests that need instanced drawing of textured quads.
*
* @author Mark Callow, github.com/MarkCallow.
*
* @par Acknowledgement
* Thanks to Sascha Willems' - www.saschawillems.de - for the concept,
* the VulkanTextOverlay class and the shaders used by this test.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <algorithm>
#include <time.h>
#include <vector>
#include "argparser.h"
#include "InstancedSampleBase.h"
#include "ltexceptions.h"
#include "VulkanTextureTranscoder.hpp"
#define VERTEX_BUFFER_BIND_ID 0
#define ENABLE_VALIDATION false
// Vertex layout for this example
struct TAVertex {
float pos[3];
float uv[2];
};
InstancedSampleBase::InstancedSampleBase(VulkanContext& vkctx,
uint32_t width, uint32_t height,
const char* const szArgs, const std::string sBasePath)
: VulkanLoadTestSample(vkctx, width, height, sBasePath)
{
zoom = -15.0f;
rotationSpeed = 0.25f;
rotation = { -15.0f, 35.0f, 0.0f };
tiling = vk::ImageTiling::eOptimal;
uboVS.instance = nullptr;
transcoded = false;
ktxVulkanDeviceInfo vdi;
ktxVulkanDeviceInfo_Construct(&vdi, vkctx.gpu, vkctx.device,
vkctx.queue, vkctx.commandPool, nullptr);
processArgs(szArgs);
KTX_error_code ktxresult;
ktxTexture* kTexture;
std::string ktxfilepath = externalFile ? ktxfilename
: getAssetPath() + ktxfilename;
ktxresult = ktxTexture_CreateFromNamedFile(ktxfilepath.c_str(),
KTX_TEXTURE_CREATE_NO_FLAGS,
&kTexture);
if (KTX_SUCCESS != ktxresult) {
std::stringstream message;
message << "Creation of ktxTexture from \"" << ktxfilepath
<< "\" failed: " << ktxErrorString(ktxresult);
throw std::runtime_error(message.str());
}
if (ktxTexture_NeedsTranscoding(kTexture)) {
TextureTranscoder tc(vkctx);
tc.transcode((ktxTexture2*)kTexture);
transcoded = true;
}
vk::Format vkFormat
= static_cast<vk::Format>(ktxTexture_GetVkFormat(kTexture));
transcodedFormat = vkFormat;
vk::ImageType imageType;
vk::ImageFormatProperties imageFormatProperties;
vk::ImageCreateFlags createFlags;
vk::ImageUsageFlags usageFlags = vk::ImageUsageFlagBits::eSampled;
uint32_t numLevels;
switch (kTexture->numDimensions) {
case 1:
imageType = vk::ImageType::e1D;
break;
case 2:
default: // To keep compilers happy.
imageType = vk::ImageType::e2D;
break;
case 3:
if (kTexture->isArray) {
std::stringstream message;
message << "Texture in \"" << getAssetPath() << szArgs
<< "\" is a 3D array texture which are not supported by Vulkan.";
ktxTexture_Destroy(kTexture);
throw std::runtime_error(message.str());
}
imageType = vk::ImageType::e3D;
break;
}
if (tiling == vk::ImageTiling::eOptimal) {
// Ensure we can copy from staging buffer to image.
usageFlags |= vk::ImageUsageFlagBits::eTransferDst;
}
if (kTexture->generateMipmaps) {
// Ensure we can blit between levels.
usageFlags |= (vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eTransferSrc);
}
vk::Result res
= vkctx.gpu.getImageFormatProperties(vkFormat, imageType, tiling,
usageFlags, createFlags,
&imageFormatProperties);
if (res != vk::Result::eSuccess) {
if (res == vk::Result::eErrorFormatNotSupported) {
throw unsupported_ttype();
} else {
throw bad_vulkan_alloc((int)res, "device.getImageFormatProperties");
}
}
numLevels = kTexture->numLevels;
if (kTexture->generateMipmaps) {
uint32_t max_dim = std::max(std::max(kTexture->baseWidth, kTexture->baseHeight), kTexture->baseDepth);
numLevels = (uint32_t)floor(log2(max_dim)) + 1;
}
if (numLevels > imageFormatProperties.maxMipLevels) {
ktxTexture_Destroy(kTexture);
throw unsupported_ttype();
}
if (kTexture->isArray
&& kTexture->numLevels > imageFormatProperties.maxArrayLayers)
{
ktxTexture_Destroy(kTexture);
throw unsupported_ttype();
}
vk::FormatProperties properties;
vkctx.gpu.getFormatProperties(vkFormat, &properties);
vk::FormatFeatureFlags features = tiling == vk::ImageTiling::eLinear ?
properties.linearTilingFeatures :
properties.optimalTilingFeatures;
vk::FormatFeatureFlags neededFeatures =
vk::FormatFeatureFlagBits::eSampledImage;
if (kTexture->numLevels > 1) {
neededFeatures |=
vk::FormatFeatureFlagBits::eSampledImageFilterLinear;
}
if (kTexture->generateMipmaps) {
neededFeatures |= vk::FormatFeatureFlagBits::eBlitDst
| vk::FormatFeatureFlagBits::eBlitSrc
| vk::FormatFeatureFlagBits::eSampledImageFilterLinear;
}
if ((features & neededFeatures) != neededFeatures) {
ktxTexture_Destroy(kTexture);
throw unsupported_ttype();
}
if (features & vk::FormatFeatureFlagBits::eSampledImageFilterLinear)
filter = vk::Filter::eLinear;
else
filter = vk::Filter::eNearest;
ktxresult =
ktxTexture_VkUploadEx(kTexture, &vdi, &texture,
static_cast<VkImageTiling>(tiling),
VK_IMAGE_USAGE_SAMPLED_BIT,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
if (KTX_SUCCESS != ktxresult) {
std::stringstream message;
message << "ktxTexture_VkUpload failed: " << ktxErrorString(ktxresult);
throw std::runtime_error(message.str());
}
// Checking if KVData contains keys of interest would go here.
ktxTexture_Destroy(kTexture);
ktxVulkanDeviceInfo_Destruct(&vdi);
}
InstancedSampleBase::~InstancedSampleBase()
{
cleanup();
}
void
InstancedSampleBase::resize(uint32_t width, uint32_t height)
{
this->w_width = width;
this->w_height = height;
vkctx.destroyDrawCommandBuffers();
vkctx.createDrawCommandBuffers();
buildCommandBuffers();
updateUniformBufferMatrices();
}
void
InstancedSampleBase::run(uint32_t /*msTicks*/)
{
// Nothing to do since the scene is not animated.
// VulkanLoadTests base class redraws from the command buffer we built.
}
//===================================================================
void
InstancedSampleBase::processArgs(std::string sArgs)
{
// Options descriptor
struct argparser::option longopts[] = {
{"external", argparser::option::no_argument, &externalFile, 1},
{"linear-tiling", argparser::option::no_argument, (int*)&tiling, (int)vk::ImageTiling::eLinear},
{NULL, argparser::option::no_argument, NULL, 0}
};
argvector argv(sArgs);
argparser ap(argv);
int ch;
while ((ch = ap.getopt(nullptr, longopts, nullptr)) != -1) {
switch (ch) {
case 0: break;
default: assert(false); // Error in args in sample table.
}
}
assert(ap.optind < argv.size());
ktxfilename = argv[ap.optind];
}
/* ------------------------------------------------------------------------- */
void
InstancedSampleBase::cleanup()
{
// Clean up used Vulkan resources
// Clean up texture resources
if (sampler)
vkctx.device.destroySampler(sampler);
if (imageView)
vkctx.device.destroyImageView(imageView);
ktxVulkanTexture_Destruct(&texture, vkctx.device, nullptr);
if (pipelines.solid)
vkctx.device.destroyPipeline(pipelines.solid);
if (pipelineLayout)
vkctx.device.destroyPipelineLayout(pipelineLayout);
if (descriptorSetLayout)
vkctx.device.destroyDescriptorSetLayout(descriptorSetLayout);
vkctx.destroyDrawCommandBuffers();
quad.freeResources(vkctx.device);
uniformDataVS.freeResources(vkctx.device);
delete[] uboVS.instance;
}
void
InstancedSampleBase::buildCommandBuffers()
{
vk::CommandBufferBeginInfo cmdBufInfo({}, nullptr);
vk::ClearValue clearValues[2];
clearValues[0].color = defaultClearColor;
clearValues[1].depthStencil = vk::ClearDepthStencilValue(1.0f, 0);
vk::RenderPassBeginInfo renderPassBeginInfo(vkctx.renderPass,
nullptr,
{{0, 0}, {w_width, w_height}},
2,
clearValues);
for (uint32_t i = 0; i < vkctx.drawCmdBuffers.size(); ++i)
{
// Set target frame buffer
renderPassBeginInfo.framebuffer = vkctx.framebuffers[i];
VK_CHECK_RESULT(vkBeginCommandBuffer(vkctx.drawCmdBuffers[i],
&static_cast<const VkCommandBufferBeginInfo&>(cmdBufInfo)));
vkCmdBeginRenderPass(vkctx.drawCmdBuffers[i],
&static_cast<const VkRenderPassBeginInfo&>(renderPassBeginInfo),
VK_SUBPASS_CONTENTS_INLINE);
vk::Viewport viewport(0, 0,
(float)w_width, (float)w_height,
0.0f, 1.0f);
vkCmdSetViewport(vkctx.drawCmdBuffers[i], 0, 1,
&static_cast<const VkViewport&>(viewport));
vk::Rect2D scissor({0, 0}, {w_width, w_height});
vkCmdSetScissor(vkctx.drawCmdBuffers[i], 0, 1,
&static_cast<const VkRect2D&>(scissor));
setSubclassPushConstants(i);
vkCmdBindDescriptorSets(vkctx.drawCmdBuffers[i],
VK_PIPELINE_BIND_POINT_GRAPHICS,
pipelineLayout, 0, 1,
&static_cast<const VkDescriptorSet&>(descriptorSet),
0, NULL);
VkDeviceSize offsets[1] = { 0 };
vkCmdBindVertexBuffers(vkctx.drawCmdBuffers[i],
VERTEX_BUFFER_BIND_ID, 1,
&static_cast<const VkBuffer&>(quad.vertices.buf),
offsets);
vkCmdBindIndexBuffer(vkctx.drawCmdBuffers[i], quad.indices.buf,
0, VK_INDEX_TYPE_UINT32);
vkCmdBindPipeline(vkctx.drawCmdBuffers[i],
VK_PIPELINE_BIND_POINT_GRAPHICS,
pipelines.solid);
vkCmdDrawIndexed(vkctx.drawCmdBuffers[i], quad.indexCount,
instanceCount, 0, 0, 0);
vkCmdEndRenderPass(vkctx.drawCmdBuffers[i]);
VK_CHECK_RESULT(vkEndCommandBuffer(vkctx.drawCmdBuffers[i]));
}
}
// Setup vertices for a single uv-mapped quad
void
InstancedSampleBase::generateQuad()
{
#define dim 2.5f
std::vector<TAVertex> vertexBuffer =
{
{ { dim, dim, 0.0f }, { 1.0f, 1.0f } },
{ { -dim, dim, 0.0f }, { 0.0f, 1.0f } },
{ { -dim, -dim, 0.0f }, { 0.0f, 0.0f } },
{ { dim, -dim, 0.0f }, { 1.0f, 0.0f } }
};
#undef dim
vkctx.createBuffer(
vk::BufferUsageFlagBits::eVertexBuffer,
vertexBuffer.size() * sizeof(TAVertex),
vertexBuffer.data(),
&quad.vertices.buf,
&quad.vertices.mem);
// Setup indices
std::vector<uint32_t> indexBuffer = { 0,1,2, 2,3,0 };
quad.indexCount = static_cast<uint32_t>(indexBuffer.size());
vkctx.createBuffer(
vk::BufferUsageFlagBits::eIndexBuffer,
indexBuffer.size() * sizeof(uint32_t),
indexBuffer.data(),
&quad.indices.buf,
&quad.indices.mem);
}
void
InstancedSampleBase::setupVertexDescriptions()
{
// Binding description
vertices.bindingDescriptions.resize(1);
vertices.bindingDescriptions[0] =
vk::VertexInputBindingDescription(
VERTEX_BUFFER_BIND_ID,
sizeof(TAVertex),
vk::VertexInputRate::eVertex);
// Attribute descriptions
// Describes memory layout and shader positions
vertices.attributeDescriptions.resize(2);
// Location 0 : Position
vertices.attributeDescriptions[0] =
vk::VertexInputAttributeDescription(
0,
VERTEX_BUFFER_BIND_ID,
vk::Format::eR32G32B32Sfloat,
0);
// Location 1 : Texture coordinates
vertices.attributeDescriptions[1] =
vk::VertexInputAttributeDescription(
1,
VERTEX_BUFFER_BIND_ID,
vk::Format::eR32G32Sfloat,
sizeof(float) * 3);
vertices.inputState = vk::PipelineVertexInputStateCreateInfo();
vertices.inputState.vertexBindingDescriptionCount =
static_cast<uint32_t>(vertices.bindingDescriptions.size());
vertices.inputState.pVertexBindingDescriptions = vertices.bindingDescriptions.data();
vertices.inputState.vertexAttributeDescriptionCount =
static_cast<uint32_t>(vertices.attributeDescriptions.size());
vertices.inputState.pVertexAttributeDescriptions = vertices.attributeDescriptions.data();
}
void
InstancedSampleBase::setupDescriptorPool()
{
// Example uses one ubo and one image sampler
std::vector<vk::DescriptorPoolSize> poolSizes =
{
{vk::DescriptorType::eUniformBuffer, 1},
{vk::DescriptorType::eCombinedImageSampler, 1}
};
vk::DescriptorPoolCreateInfo descriptorPoolInfo(
{},
2,
static_cast<uint32_t>(poolSizes.size()),
poolSizes.data());
vk::Result res = vkctx.device.createDescriptorPool(&descriptorPoolInfo,
nullptr,
&descriptorPool);
if (res != vk::Result::eSuccess) {
throw bad_vulkan_alloc((int)res, "createDescriptorPool");
}
}
void
InstancedSampleBase::setupDescriptorSetLayout()
{
DescriptorBindings descriptorBindings =
{
// Binding 0 : Vertex shader uniform buffer
{0,
vk::DescriptorType::eUniformBuffer,
1,
vk::ShaderStageFlagBits::eVertex},
// Binding 1 : Fragment shader image sampler
{1,
vk::DescriptorType::eCombinedImageSampler,
1,
vk::ShaderStageFlagBits::eFragment},
};
//addSubclassDescriptors(descriptorBindings);
vk::DescriptorSetLayoutCreateInfo descriptorLayout(
{},
static_cast<uint32_t>(descriptorBindings.size()),
descriptorBindings.data());
vk::Result res
= vkctx.device.createDescriptorSetLayout(&descriptorLayout,
nullptr,
&descriptorSetLayout);
if (res != vk::Result::eSuccess) {
throw bad_vulkan_alloc((int)res, "createDescriptorSetLayout");
}
vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo(
{},
1,
&descriptorSetLayout);
std::vector<vk::PushConstantRange> pushConstantRanges;
addSubclassPushConstantRanges(pushConstantRanges);
if (pushConstantRanges.size() > 0) {
pipelineLayoutCreateInfo.setPushConstantRanges(pushConstantRanges);
}
res = vkctx.device.createPipelineLayout(&pipelineLayoutCreateInfo,
nullptr,
&pipelineLayout);
if (res != vk::Result::eSuccess) {
throw bad_vulkan_alloc((int)res, "createPipelineLayout");
}
}
void
InstancedSampleBase::setupDescriptorSet()
{
vk::DescriptorSetAllocateInfo allocInfo(
descriptorPool,
1,
&descriptorSetLayout);
vk::Result res
= vkctx.device.allocateDescriptorSets(&allocInfo, &descriptorSet);
if (res != vk::Result::eSuccess) {
throw bad_vulkan_alloc((int)res, "allocateDescriptorSets");
}
// Image descriptor for the color map texture
vk::DescriptorImageInfo texDescriptor(
sampler,
imageView,
vk::ImageLayout::eShaderReadOnlyOptimal);
std::vector<vk::WriteDescriptorSet> writeDescriptorSets;
// Binding 0 : Vertex shader uniform buffer
writeDescriptorSets.push_back(vk::WriteDescriptorSet(
descriptorSet,
0,
0,
1,
vk::DescriptorType::eUniformBuffer,
nullptr,
&uniformDataVS.descriptor)
);
// Binding 1 : Fragment shader texture sampler
writeDescriptorSets.push_back(vk::WriteDescriptorSet(
descriptorSet,
1,
0,
1,
vk::DescriptorType::eCombinedImageSampler,
&texDescriptor)
);
vkctx.device.updateDescriptorSets(
static_cast<uint32_t>(writeDescriptorSets.size()),
writeDescriptorSets.data(),
0,
nullptr);
}
void
InstancedSampleBase::preparePipelines(const char* const fragShaderName,
const char* const vertShaderName,
uint32_t instanceCountConstId)
{
vk::PipelineInputAssemblyStateCreateInfo inputAssemblyState(
{},
vk::PrimitiveTopology::eTriangleList);
vk::PipelineRasterizationStateCreateInfo rasterizationState;
// Must be false because we haven't enabled the depthClamp device feature.
rasterizationState.depthClampEnable = false;
rasterizationState.rasterizerDiscardEnable = false;
rasterizationState.polygonMode = vk::PolygonMode::eFill;
rasterizationState.cullMode = vk::CullModeFlagBits::eNone;
rasterizationState.frontFace = vk::FrontFace::eCounterClockwise;
rasterizationState.lineWidth = 1.0f;
vk::PipelineColorBlendAttachmentState blendAttachmentState;
blendAttachmentState.blendEnable = false;
//blendAttachmentState.colorWriteMask = 0xf;
blendAttachmentState.colorWriteMask = vk::ColorComponentFlagBits::eR
| vk::ColorComponentFlagBits::eG
| vk::ColorComponentFlagBits::eB
| vk::ColorComponentFlagBits::eA;
vk::PipelineColorBlendStateCreateInfo colorBlendState;
colorBlendState.attachmentCount = 1;
colorBlendState.pAttachments = &blendAttachmentState;
vk::PipelineDepthStencilStateCreateInfo depthStencilState;
depthStencilState.depthTestEnable = true;
depthStencilState.depthWriteEnable = true;
depthStencilState.depthCompareOp = vk::CompareOp::eLessOrEqual;
vk::PipelineViewportStateCreateInfo viewportState;
viewportState.viewportCount = 1;
viewportState.scissorCount = 1;
vk::PipelineMultisampleStateCreateInfo multisampleState;
multisampleState.rasterizationSamples = vk::SampleCountFlagBits::e1;
std::vector<vk::DynamicState> dynamicStateEnables = {
vk::DynamicState::eViewport,
vk::DynamicState::eScissor
};
vk::PipelineDynamicStateCreateInfo dynamicState(
{},
static_cast<uint32_t>(dynamicStateEnables.size()),
dynamicStateEnables.data());
// Load shaders
std::array<vk::PipelineShaderStageCreateInfo,2> shaderStages;
std::string filepath = getAssetPath();
// What a lot of code to set a single constant value.
vk::SpecializationInfo specializationInfo;
vk::SpecializationMapEntry mapEntries[1];
mapEntries[0].setConstantID(instanceCountConstId);
mapEntries[0].setOffset(0);
mapEntries[0].setSize(4);
specializationInfo.setMapEntryCount(1);
specializationInfo.setPMapEntries(mapEntries);
specializationInfo.setPData(&instanceCount);
specializationInfo.setDataSize(sizeof(instanceCount));
shaderStages[0].pSpecializationInfo = &specializationInfo;
shaderStages[0] = loadShader(filepath + vertShaderName,
vk::ShaderStageFlagBits::eVertex);
shaderStages[1] = loadShader(filepath + fragShaderName,
vk::ShaderStageFlagBits::eFragment);
vk::GraphicsPipelineCreateInfo pipelineCreateInfo;
pipelineCreateInfo.layout = pipelineLayout;
pipelineCreateInfo.renderPass = vkctx.renderPass;
pipelineCreateInfo.pVertexInputState = &vertices.inputState;
pipelineCreateInfo.pInputAssemblyState = &inputAssemblyState;
pipelineCreateInfo.pRasterizationState = &rasterizationState;
pipelineCreateInfo.pColorBlendState = &colorBlendState;
pipelineCreateInfo.pMultisampleState = &multisampleState;
pipelineCreateInfo.pViewportState = &viewportState;
pipelineCreateInfo.pDepthStencilState = &depthStencilState;
pipelineCreateInfo.pDynamicState = &dynamicState;
pipelineCreateInfo.stageCount = (uint32_t)shaderStages.size();
pipelineCreateInfo.pStages = shaderStages.data();
vk::Result res
= vkctx.device.createGraphicsPipelines(vkctx.pipelineCache, 1,
&pipelineCreateInfo, nullptr,
&pipelines.solid);
if (res != vk::Result::eSuccess) {
throw bad_vulkan_alloc((int)res, "createGraphicsPipelines");
}
}
#define _PAD16(nbytes) (ktx_uint32_t)(16 * ceilf((float)(nbytes) / 16))
void
InstancedSampleBase::prepareUniformBuffers(uint32_t shaderDeclaredInstances)
{
uboVS.instance = new UboInstanceData[instanceCount];
// Elements of the array of UboInstanceData will be aligned on 16-byte
// boundaries per the std140 rule for mat4/vec4. _PAD16 is unnecessary
// right now but will become so if anything is added to the ubo before
// the UboInstanceData. _PAD16 is put here as a warning.
uint32_t uboSize = _PAD16(sizeof(uboVS.matrices))
//+ instanceCount * sizeof(UboInstanceData);
+ shaderDeclaredInstances * sizeof(UboInstanceData);
// Vertex shader uniform buffer block
vkctx.createBuffer(
vk::BufferUsageFlagBits::eUniformBuffer,
vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent,
uboSize,
nullptr,
&uniformDataVS.buffer,
&uniformDataVS.memory,
&uniformDataVS.descriptor);
// MoltenVK can't specialize array-length constants, an MSL limitation,
// so we have to potentially modify instanceCount. We can't just
// declare a very long array in the shaders because we get a MoltenVK
// validation error when the allocation we make above is less than
// the declared length. Making the array length 1, works on macOS
// but not on iOS where only 1 instance is drawn correctly. See
// MoltenVK issues 1420 and 1421.
// https://github.com/KhronosGroup/MoltenVK/issues/1421.
// Paren around std::min avoids a SNAFU that windef.h has a "min" macro.
instanceCount = (std::min)(shaderDeclaredInstances, instanceCount);
// Array indices and model matrices are fixed
float offset = -1.5f;
float center = (instanceCount * offset) / 2;
for (uint32_t i = 0; i < instanceCount; i++)
{
// Instance model matrix
uboVS.instance[i].model = glm::translate(glm::mat4(), glm::vec3(0.0f, i * offset - center, 0.0f));
uboVS.instance[i].model = glm::rotate(uboVS.instance[i].model, glm::radians(60.0f), glm::vec3(1.0f, 0.0f, 0.0f));
}
// Update instanced part of the uniform buffer
uint8_t *pData;
// N.B. See comment re _PAD16 before uboSize above.
uint32_t dataOffset = _PAD16(sizeof(uboVS.matrices));
uint32_t dataSize = instanceCount * sizeof(UboInstanceData);
VK_CHECK_RESULT(vkMapMemory(vkctx.device, uniformDataVS.memory, dataOffset, dataSize, 0, (void **)&pData));
memcpy(pData, uboVS.instance, dataSize);
vkUnmapMemory(vkctx.device, uniformDataVS.memory);
updateUniformBufferMatrices();
}
void
InstancedSampleBase::updateUniformBufferMatrices()
{
// Only updates the uniform buffer block part containing the global matrices
// Projection
uboVS.matrices.projection = glm::perspective(glm::radians(60.0f), (float)w_width / (float)w_height, 0.001f, 256.0f);
// View
uboVS.matrices.view = glm::translate(glm::mat4(), glm::vec3(0.0f, -1.0f, zoom));
uboVS.matrices.view *= glm::translate(glm::mat4(), cameraPos);
uboVS.matrices.view = glm::rotate(uboVS.matrices.view, glm::radians(rotation.x), glm::vec3(1.0f, 0.0f, 0.0f));
uboVS.matrices.view = glm::rotate(uboVS.matrices.view, glm::radians(rotation.y), glm::vec3(0.0f, 1.0f, 0.0f));
uboVS.matrices.view = glm::rotate(uboVS.matrices.view, glm::radians(rotation.z), glm::vec3(0.0f, 0.0f, 1.0f));
// Only update the matrices part of the uniform buffer
uint8_t *pData;
VK_CHECK_RESULT(vkMapMemory(vkctx.device, uniformDataVS.memory, 0, sizeof(uboVS.matrices), 0, (void **)&pData));
memcpy(pData, &uboVS.matrices, sizeof(uboVS.matrices));
vkUnmapMemory(vkctx.device, uniformDataVS.memory);
}
void
InstancedSampleBase::prepareSamplerAndView()
{
// Create sampler.
vk::SamplerCreateInfo samplerInfo;
// Set the non-default values
samplerInfo.magFilter = filter;
samplerInfo.minFilter = filter;
samplerInfo.mipmapMode = vk::SamplerMipmapMode::eLinear;
samplerInfo.maxLod = (float)texture.levelCount;
if (vkctx.gpuFeatures.samplerAnisotropy == VK_TRUE) {
samplerInfo.anisotropyEnable = VK_TRUE;
samplerInfo.maxAnisotropy = 8;
} else {
// vulkan.hpp needs fixing
samplerInfo.maxAnisotropy = 1.0;
}
samplerInfo.borderColor = vk::BorderColor::eFloatOpaqueWhite;
// To make viewer more useful in verifying the content of 3d textures.
samplerInfo.addressModeW = vk::SamplerAddressMode::eClampToEdge;
sampler = vkctx.device.createSampler(samplerInfo);
// Create image view.
// Textures are not directly accessed by the shaders and are abstracted
// by image views containing additional information and sub resource
// ranges.
vk::ImageViewCreateInfo viewInfo;
// Set the non-default values.
viewInfo.image = texture.image;
viewInfo.format = static_cast<vk::Format>(texture.imageFormat);
viewInfo.viewType
= static_cast<vk::ImageViewType>(texture.viewType);
viewInfo.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor;
viewInfo.subresourceRange.layerCount = texture.layerCount;
viewInfo.subresourceRange.levelCount = texture.levelCount;
imageView = vkctx.device.createImageView(viewInfo);
}
void
InstancedSampleBase::prepare(const char* const fragShaderName,
const char* const vertShaderName,
uint32_t instanceCountConstId,
uint32_t instanceCountIn,
uint32_t shaderDeclaredInstances)
{
this->instanceCount = instanceCountIn;
prepareSamplerAndView();
setupVertexDescriptions();
generateQuad();
prepareUniformBuffers(shaderDeclaredInstances);
setupDescriptorSetLayout();
preparePipelines(fragShaderName, vertShaderName,
instanceCountConstId);
setupDescriptorPool();
setupDescriptorSet();
vkctx.createDrawCommandBuffers();
buildCommandBuffers();
}
const char*
InstancedSampleBase::customizeTitle(const char* const baseTitle)
{
if (transcoded) {
this->title = baseTitle;
this->title += " Transcoded to ";
this->title += vkFormatString((VkFormat)transcodedFormat);
return this->title.c_str();
}
return baseTitle;
}