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

730 lines
22 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 TextureArray
* @~English
*
* @brief Test loading of 2D textures.
*
* @author Mark Callow, github.com/MarkCallow.
*
* @par Acknowledgement
* Thanks to Sascha Willems' - www.saschawillems.de - for VulkanTextOverlay.
*/
#if defined(_WIN32)
#if _MSC_VER < 1900
#define snprintf _snprintf
#endif
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <assert.h>
#include <math.h>
#include <array>
#include <vector>
#include "TexturedCube.h"
#include "cube.h"
#define UNIFORM_OFFSET(f) (size_t)(&((Uniforms *)0)->mvp)
#define VERTEX_BUFFER_FIRST_BINDING_ID 0
VulkanLoadTestSample*
TexturedCube::create(VulkanContext& vkctx,
uint32_t width, uint32_t height,
const char* const szArgs,
const std::string sBasePath)
{
return new TexturedCube(vkctx, width, height, szArgs, sBasePath);
}
TexturedCube::TexturedCube(VulkanContext& vkctx,
uint32_t width, uint32_t height,
const char* const /*szArgs*/,
const std::string sBasePath)
: VulkanLoadTestSample(vkctx, width, height, sBasePath),
numTextures(1)
{
zoom = 1.0f;
rotation = { 0.0f, 0.0f, 0.0f };
uniforms.lodBias = 0.0;
try {
prepareUniformBuffer();
prepareCubeDataBuffers();
setupVertexDescriptions();
createDescriptorSetLayout();
preparePipeline();
prepareDescriptorPool();
prepareDescriptorSet();
vkctx.createDrawCommandBuffers();
for (uint32_t i = 0; i < vkctx.swapchain.imageCount; i++) {
buildCommandBuffer(i);
}
} catch (std::exception& e) {
(void)e; // To quiet unused variable warnings from some compilers.
cleanup();
throw;
}
}
TexturedCube::~TexturedCube()
{
cleanup();
}
void
TexturedCube::resize(uint32_t width, uint32_t height)
{
w_width = width;
w_height = height;
uniforms.projection = perspective(45.f, width / (float)height, 1.f, 100.f);
vkctx.destroyDrawCommandBuffers();
vkctx.createDrawCommandBuffers();
for (uint32_t i = 0; i < vkctx.swapchain.imageCount; i++) {
buildCommandBuffer(i);
}
}
void
TexturedCube::run(uint32_t msTicks)
{
/* Setup the view matrix : just turn around the cube. */
vec3 up(0.f, 1.f, 0.f);
const float fDistance = 5.0f;
vec3 eye((float)cos( msTicks*0.001f ) * fDistance,
(float)sin( msTicks*0.0007f ) * fDistance,
(float)sin( msTicks*0.001f ) * fDistance);
vec3 center = vec3(0.f, 0.f, 0.f);
uniforms.viewPos = vec4(eye, 1.0);
uniforms.view = lookAt(eye, center, up);
updateUniformBuffer();
}
/* ------------------------------------------------------------------------- */
void
TexturedCube::cleanup()
{
}
void
TexturedCube::buildCommandBuffer(const int bufferIndex)
{
const VkCommandBuffer& cmdBuf = vkctx.drawCmdBuffers[bufferIndex];
const VkCommandBufferBeginInfo cmd_buf_info = {
VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, NULL, 0, NULL
};
#if 0
typedef union VkClearColorValue {
float float32[4];
int32_t int32[4];
uint32_t uint32[4];
} VkClearColorValue;
typedef struct VkClearDepthStencilValue {
float depth;
uint32_t stencil;
} VkClearDepthStencilValue;
typedef union VkClearValue {
VkClearColorValue color;
VkClearDepthStencilValue depthStencil;
} VkClearValue;
#endif
// clang suggested all these braces. I could not find documentation of
// union initialization except using designated intializers which are not
// in C++11.
VkClearValue clear_values[2] = {
{ {{0.0f, 0.2f, 0.2f, 1.0f}} },
{ {{0.0f, 0}} }
};
const VkRenderPassBeginInfo rp_begin = {
VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
NULL,
vkctx.renderPass,
vkctx.framebuffers[bufferIndex],
{ {0, 0}, {w_width, w_height} },
ARRAY_LEN(clear_values),
clear_values,
};
VK_CHECK_RESULT(vkBeginCommandBuffer(cmdBuf, &cmd_buf_info));
vkCmdBeginRenderPass(cmdBuf, &rp_begin,
VK_SUBPASS_CONTENTS_INLINE);
VkViewport viewport = {
0,
0,
(float)w_width,
(float)w_height,
0.0f,
1.0f
};
vkCmdSetViewport(cmdBuf, 0, 1, &viewport);
VkRect2D scissor;
scissor.offset.x = 0;
scissor.offset.y = 0;
scissor.extent.width = w_width;
scissor.extent.height = w_height;
vkCmdSetScissor(cmdBuf, 0, 1, &scissor);
vkCmdBindDescriptorSets(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS,
pipelineLayout, 0, 1, &descriptorSet, 0, NULL);
vkCmdBindPipeline(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
VkDeviceSize offsets[1] = { 0 };
vkCmdBindVertexBuffers(cmdBuf, VERTEX_BUFFER_FIRST_BINDING_ID, 1,
&static_cast<const VkBuffer&>(vertices.buf),
offsets);
vkCmdBindIndexBuffer(cmdBuf, indices.buf, 0, VK_INDEX_TYPE_UINT16);
vkCmdDrawIndexed(cmdBuf, indices.count, 1, 0, 0, 0);
vkCmdEndRenderPass(vkctx.drawCmdBuffers[bufferIndex]);
VK_CHECK_RESULT(vkEndCommandBuffer(cmdBuf));
}
void
TexturedCube::prepareUniformBuffer()
{
vkctx.createBuffer(
vk::BufferUsageFlagBits::eUniformBuffer,
sizeof(Uniforms),
&uniforms,
&uniformData.buffer,
&uniformData.memory,
&uniformData.descriptor);
}
void
TexturedCube::updateUniformBuffer()
{
#if 0 /* Move to run? */
mat4 viewMatrix = glm::translate(mat4(), vec3(0.0f, 0.0f, zoom));
uniforms.model = viewMatrix * translate(mat4(), cameraPos);
uniforms.model = rotate(uniforms.model, rotation.x, vec3(1.0f, 0.0f, 0.0f));
uniforms.model = rotate(uniforms.model, rotation.y, vec3(0.0f, 1.0f, 0.0f));
uniforms.model = rotate(uniforms.model, rotation.z, vec3(0.0f, 0.0f, 1.0f));
uniforms.viewPos = glm::vec4(0.0f, 0.0f, -zoom, 0.0f);
#endif
uint8_t *pData;
VK_CHECK_RESULT(vkMapMemory(vkctx.device, uniformData.memory, 0,
sizeof(uniforms), 0, (void **)&pData));
memcpy(pData, &uniforms, sizeof(uniforms));
vkUnmapMemory(vkctx.device, uniformData.memory);
}
struct Vertex {
float position[3];
float normal[3];
float uv[2];
float color[4];
};
void
TexturedCube::prepareCubeDataBuffers()
{
#if 1
std::vector<float> vertexBuffer;
vertexBuffer.insert(vertexBuffer.end(),
&cube_face[0],
&cube_face[ARRAY_LEN(cube_face)]);
#if 0
vertexBuffer.insert(vertexBuffer.end(),
&cube_normal[0],
&cube_normal[ARRAY_LEN(cube_normal)]);
vertexBuffer.insert(vertexBuffer.end(),
&cube_texture[0],
&cube_texture[ARRAY_LEN(cube_texture)]);
vertexBuffer.insert(vertexBuffer.end(),
&cube_color[0],
&cube_color[ARRAY_LEN(cube_color)]);
#endif
vkctx.createBuffer(
vk::BufferUsageFlagBits::eVertexBuffer,
vertexBuffer.size() * sizeof(float),
vertexBuffer.data(),
&vertices.buf,
&vertices.mem);
#else
// It appears to be impossible in Vulkan to have multiple non-interleaved
// attributes in 1 buffer object. So interleave the data.
std::vector<Vertex> vertexBuffer;
uint i, j, k;
for (i = 0, j = 0, k = 0; i < ARRAY_LEN(cube_face); i++, j++, k++) {
Vertex vertex;
vertex.position[0] = cube_face[i];
vertex.position[1] = cube_face[i+1];
vertex.position[2] = cube_face[i+2];
vertex.normal[0] = cube_normal[i];
vertex.normal[1] = cube_normal[++i];
vertex.normal[2] = cube_normal[++i];
vertex.uv[0] = cube_texture[j];
vertex.uv[1] = cube_texture[++j];
vertex.color[0] = cube_color[k];
vertex.color[1] = cube_color[++k];
vertex.color[2] = cube_color[++k];
vertex.color[3] = cube_color[++k];
vertexBuffer.push_back(vertex);
}
createBuffer(
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
vertexBuffer.size() * sizeof(Vertex),
vertexBuffer.data(),
&vertices.buf,
&vertices.mem);
#endif
// Setup indices
indices.count = ARRAY_LEN(cube_index_buffer);
vkctx.createBuffer(
vk::BufferUsageFlagBits::eIndexBuffer,
sizeof(cube_index_buffer),
(void*)cube_index_buffer,
&indices.buf,
&indices.mem);
}
static VkVertexInputBindingDescription
initVertexInputBindingDescription(uint32_t binding,
uint32_t stride,
VkVertexInputRate inputRate)
{
VkVertexInputBindingDescription vInputBindDescription = {};
vInputBindDescription.binding = binding;
vInputBindDescription.stride = stride;
vInputBindDescription.inputRate = inputRate;
return vInputBindDescription;
}
static VkVertexInputAttributeDescription
initVertexInputAttributeDescription(uint32_t binding,
uint32_t location,
VkFormat format,
uint32_t offset)
{
VkVertexInputAttributeDescription vInputAttribDescription = {};
vInputAttribDescription.location = location;
vInputAttribDescription.binding = binding;
vInputAttribDescription.format = format;
vInputAttribDescription.offset = offset;
return vInputAttribDescription;
}
static VkPipelineVertexInputStateCreateInfo
initPipelineVertexInputStateCreateInfo()
{
VkPipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo = {};
pipelineVertexInputStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
pipelineVertexInputStateCreateInfo.pNext = NULL;
return pipelineVertexInputStateCreateInfo;
}
void
TexturedCube::setupVertexDescriptions()
{
// Binding description
vertices.bindingDescriptions.resize(1);
vertices.bindingDescriptions[0] =
initVertexInputBindingDescription(
VERTEX_BUFFER_FIRST_BINDING_ID,
/*sizeof(Vertex),*/CUBE_FACE_STRIDE,
VK_VERTEX_INPUT_RATE_VERTEX);
// Attribute descriptions
// Describes memory layout and shader positions
vertices.attributeDescriptions.resize(1);
//vertices.attributeDescriptions.resize(4);
// Location 0 : position
vertices.attributeDescriptions[0] =
initVertexInputAttributeDescription(
VERTEX_BUFFER_FIRST_BINDING_ID,
0,
VK_FORMAT_R32G32B32_SFLOAT,
0);
#if 0
// Location 1 : vertex normal
vertices.attributeDescriptions[1] =
initVertexInputAttributeDescription(
VERTEX_BUFFER_FIRST_BINDING_ID,
1,
VK_FORMAT_R32G32B32_SFLOAT,
sizeof(float)*3);//sizeof(cube_face));
// Location 2 : texture coordinates
vertices.attributeDescriptions[2] =
initVertexInputAttributeDescription(
VERTEX_BUFFER_FIRST_BINDING_ID,
2,
VK_FORMAT_R32G32_SFLOAT,
sizeof(float)*6);//sizeof(cube_face) + sizeof(cube_normal));
// Location 3 : colors
vertices.attributeDescriptions[3] =
initVertexInputAttributeDescription(
VERTEX_BUFFER_FIRST_BINDING_ID,
3,
VK_FORMAT_R32G32B32_SFLOAT,
sizeof(float)*6 + sizeof(float)*2);//sizeof(cube_face) + sizeof(cube_normal) + sizeof(cube_texture));
#endif
vertices.inputState = initPipelineVertexInputStateCreateInfo();
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
TexturedCube::createDescriptorSetLayout()
{
const VkDescriptorSetLayoutBinding layoutBindings[1] = {
{
0,
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
1,
VK_SHADER_STAGE_VERTEX_BIT,
NULL,
},
#if 0
{
1,
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
numTextures,
VK_SHADER_STAGE_FRAGMENT_BIT,
NULL,
},
#endif
};
const VkDescriptorSetLayoutCreateInfo dslcInfo = {
VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
NULL,
0,
1,//2,
layoutBindings,
};
VK_CHECK_RESULT(vkCreateDescriptorSetLayout(vkctx.device,
&dslcInfo, NULL,
&descriptorSetLayout));
}
void
TexturedCube::preparePipeline()
{
const VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = {
VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
NULL,
0,
1,
&descriptorSetLayout,
0,
NULL,
};
VK_CHECK_RESULT(vkCreatePipelineLayout(vkctx.device,
&pipelineLayoutCreateInfo,
NULL,
&pipelineLayout));
VkPipelineInputAssemblyStateCreateInfo ias = { };
ias.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
ias.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
VkPipelineRasterizationStateCreateInfo rs = { };
rs.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rs.polygonMode = VK_POLYGON_MODE_FILL;
rs.cullMode = VK_CULL_MODE_NONE; //VK_CULL_MODE_BACK_BIT;
rs.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
// Must be false because we haven't enabled the depthClamp device feature.
rs.depthClampEnable = VK_FALSE;
rs.rasterizerDiscardEnable = VK_FALSE;
rs.depthBiasEnable = VK_FALSE;
rs.lineWidth = 1.0f;
VkPipelineColorBlendAttachmentState cbas = { };
cbas.colorWriteMask = 0xf;
cbas.blendEnable = VK_FALSE;
VkPipelineColorBlendStateCreateInfo cbs = { };
cbs.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
cbs.attachmentCount = 1;
cbs.pAttachments = &cbas;
VkPipelineDepthStencilStateCreateInfo dss = { };
dss.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
dss.depthTestEnable = VK_TRUE;
dss.depthWriteEnable = VK_TRUE;
dss.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL;
dss.depthBoundsTestEnable = VK_FALSE;
dss.back.failOp = VK_STENCIL_OP_KEEP;
dss.back.passOp = VK_STENCIL_OP_KEEP;
dss.back.compareOp = VK_COMPARE_OP_ALWAYS;
dss.stencilTestEnable = VK_FALSE;
dss.front = dss.back;
VkPipelineViewportStateCreateInfo vps = { };
vps.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
vps.viewportCount = 1;
vps.scissorCount = 1;
std::vector<VkDynamicState> dynamicStateEnables = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR
};
VkPipelineDynamicStateCreateInfo ds = { };
ds.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
ds.pDynamicStates = dynamicStateEnables.data();
ds.dynamicStateCount = static_cast<uint32_t>(dynamicStateEnables.size());
VkPipelineMultisampleStateCreateInfo mss = { };
mss.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
mss.pSampleMask = NULL;
mss.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
// Load shaders
// Two stages: vs and fs
//std::array<vk::PipelineShaderStageCreateInfo,2> shaderStages;
std::array<VkPipelineShaderStageCreateInfo,2> shaderStages;
#if defined(INCLUDE_SHADERS)
shaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
//shaderStages[0].pNext = NULL;
//shaderStages[0].flags = 0;
shaderStages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
shaderStages[0].module = prepareVertShader();
shaderStages[0].pName = "main";
//shaderStages[0].pSpecializationInfo = nullptr;
shaderStages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
//shaderStages[1].pNext = NULL;
//shaderStages[1].flags = 0;
shaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
shaderStages[1].module = prepareFragShader();
shaderStages[1].pName = "main";
//shaderStages[1].pSpecializationInfo = nullptr;
#else
std::string filepath = getAssetPath();
shaderStages[0] = static_cast<VkPipelineShaderStageCreateInfo>(loadShader(filepath + "cube.vert.spv",
vk::ShaderStageFlagBits::eVertex));
shaderStages[1] = static_cast<VkPipelineShaderStageCreateInfo>(loadShader(filepath + "cube.frag.spv",
vk::ShaderStageFlagBits::eFragment));
#endif
VkPipelineCacheCreateInfo pc { };
pc.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
VK_CHECK_RESULT(vkCreatePipelineCache(vkctx.device, &pc, NULL,
&pipelineCache));
VkGraphicsPipelineCreateInfo pipelineCreateInfo { };
pipelineCreateInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineCreateInfo.renderPass = vkctx.renderPass;
pipelineCreateInfo.layout = pipelineLayout;
pipelineCreateInfo.pVertexInputState =
&static_cast<const VkPipelineVertexInputStateCreateInfo&>(vertices.inputState);
pipelineCreateInfo.pInputAssemblyState = &ias;
pipelineCreateInfo.pRasterizationState = &rs;
pipelineCreateInfo.pColorBlendState = &cbs;
pipelineCreateInfo.pMultisampleState = &mss;
pipelineCreateInfo.pViewportState = &vps;
pipelineCreateInfo.pDepthStencilState = &dss;
pipelineCreateInfo.pDynamicState = &ds;
pipelineCreateInfo.stageCount = (uint32_t)shaderStages.size();
pipelineCreateInfo.pStages = shaderStages.data();
VK_CHECK_RESULT(vkCreateGraphicsPipelines(vkctx.device,
pipelineCache,
1,
&pipelineCreateInfo, nullptr,
&pipeline));
}
#if defined(INCLUDE_SHADERS)
VkShaderModule
TexturedCube::prepareVertShader()
{
size_t codeSize;
// INCLUDE_SHADERS requires use of glslc or addition of its -mfmt=num
// option to glslangValidator. In order not to add the additional
// dependency on glslc we'll load the shaders from files.
uint32_t vert[] = {
#include "cube.vert.spv"
};
codeSize = sizeof(vert);
return createShaderModule(vert, codeSize);
}
VkShaderModule
TexturedCube::prepareFragShader()
{
size_t codeSize;
uint32_t frag[] = {
#include "cube.frag.spv"
};
codeSize = sizeof(frag);
return createShaderModule(frag, codeSize);
}
VkShaderModule
TexturedCube::createShaderModule(uint32_t* spv, size_t size)
{
VkShaderModule module;
VkShaderModuleCreateInfo shCreateInfo = {};
shCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
shCreateInfo.codeSize = size;
shCreateInfo.pCode = spv;
shCreateInfo.flags = 0;
VK_CHECK_RESULT(vkCreateShaderModule(vkctx.device, &shCreateInfo,
NULL, &module));
return module;
}
#endif
void
TexturedCube::prepareDescriptorPool() {
const VkDescriptorPoolSize typeCounts[2] = {
{
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
1,
},
{
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
numTextures,
},
};
const VkDescriptorPoolCreateInfo dpoolCreateInfo = {
VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
NULL,
0,
1,
2,
typeCounts,
};
U_ASSERT_ONLY VkResult err;
err = vkCreateDescriptorPool(vkctx.device, &dpoolCreateInfo, NULL,
&descriptorPool);
assert(!err);
}
static
VkWriteDescriptorSet initWriteDescriptorSet(
VkDescriptorSet dstSet,
VkDescriptorType type,
uint32_t binding,
const VkDescriptorBufferInfo* bufferInfo)
{
VkWriteDescriptorSet writeDescriptorSet = {};
writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeDescriptorSet.pNext = NULL;
writeDescriptorSet.dstSet = dstSet;
writeDescriptorSet.descriptorType = type;
writeDescriptorSet.dstBinding = binding;
writeDescriptorSet.pBufferInfo = bufferInfo;
// Default value in all examples
writeDescriptorSet.descriptorCount = 1;
return writeDescriptorSet;
}
void
TexturedCube::prepareDescriptorSet()
{
VkDescriptorSetAllocateInfo allocInfo = { };
allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
allocInfo.descriptorPool = descriptorPool;
allocInfo.descriptorSetCount = 1;
allocInfo.pSetLayouts = &descriptorSetLayout;
VK_CHECK_RESULT(vkAllocateDescriptorSets(vkctx.device,
&allocInfo,
&descriptorSet));
#if 0
// Image descriptor for the color map texture
vk::DescriptorImageInfo texDescriptor(
texture.sampler,
texture.view,
vk::ImageLayout::eShaderReadOnlyOptimal);
#endif
std::vector<VkWriteDescriptorSet> writeDescriptorSets =
{
// Binding 0 : Vertex shader uniform buffer
initWriteDescriptorSet(
descriptorSet,
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
0,
&static_cast<const VkDescriptorBufferInfo&>(uniformData.descriptor)),
#if 0
// Binding 1 : Fragment shader texture sampler
initWriteDescriptorSet(
descriptorSet,
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
1,
&texDescriptor)
#endif
};
vkUpdateDescriptorSets(vkctx.device,
static_cast<uint32_t>(writeDescriptorSets.size()),
writeDescriptorSets.data(),
0, NULL);
}