You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
453 lines
17 KiB
453 lines
17 KiB
// Copyright 2021 The SwiftShader Authors. All Rights Reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include "DrawTester.hpp"
|
|
|
|
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
|
|
|
|
DrawTester::DrawTester(Multisample multisample)
|
|
: multisample(multisample == Multisample::True)
|
|
{
|
|
}
|
|
|
|
DrawTester::~DrawTester()
|
|
{
|
|
device.freeCommandBuffers(commandPool, commandBuffers);
|
|
|
|
device.destroyDescriptorPool(descriptorPool);
|
|
for(auto &sampler : samplers)
|
|
{
|
|
device.destroySampler(sampler, nullptr);
|
|
}
|
|
images.clear();
|
|
device.destroyCommandPool(commandPool, nullptr);
|
|
|
|
for(auto &fence : waitFences)
|
|
{
|
|
device.destroyFence(fence, nullptr);
|
|
}
|
|
|
|
device.destroySemaphore(renderCompleteSemaphore, nullptr);
|
|
device.destroySemaphore(presentCompleteSemaphore, nullptr);
|
|
|
|
device.destroyPipeline(pipeline);
|
|
device.destroyPipelineLayout(pipelineLayout, nullptr);
|
|
device.destroyDescriptorSetLayout(descriptorSetLayout);
|
|
|
|
device.freeMemory(vertices.memory, nullptr);
|
|
device.destroyBuffer(vertices.buffer, nullptr);
|
|
|
|
for(auto &framebuffer : framebuffers)
|
|
{
|
|
framebuffer.reset();
|
|
}
|
|
|
|
device.destroyRenderPass(renderPass, nullptr);
|
|
|
|
swapchain.reset();
|
|
window.reset();
|
|
}
|
|
|
|
void DrawTester::initialize()
|
|
{
|
|
VulkanTester::initialize();
|
|
|
|
window.reset(new Window(instance, windowSize));
|
|
swapchain.reset(new Swapchain(physicalDevice, device, *window));
|
|
|
|
renderPass = createRenderPass(swapchain->colorFormat);
|
|
createFramebuffers(renderPass);
|
|
|
|
prepareVertices();
|
|
|
|
pipeline = createGraphicsPipeline(renderPass);
|
|
|
|
createSynchronizationPrimitives();
|
|
|
|
createCommandBuffers(renderPass);
|
|
}
|
|
|
|
void DrawTester::renderFrame()
|
|
{
|
|
swapchain->acquireNextImage(presentCompleteSemaphore, currentFrameBuffer);
|
|
|
|
device.waitForFences(1, &waitFences[currentFrameBuffer], VK_TRUE, UINT64_MAX);
|
|
device.resetFences(1, &waitFences[currentFrameBuffer]);
|
|
|
|
vk::PipelineStageFlags waitStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput;
|
|
|
|
vk::SubmitInfo submitInfo;
|
|
submitInfo.pWaitDstStageMask = &waitStageMask;
|
|
submitInfo.pWaitSemaphores = &presentCompleteSemaphore;
|
|
submitInfo.waitSemaphoreCount = 1;
|
|
submitInfo.pSignalSemaphores = &renderCompleteSemaphore;
|
|
submitInfo.signalSemaphoreCount = 1;
|
|
submitInfo.pCommandBuffers = &commandBuffers[currentFrameBuffer];
|
|
submitInfo.commandBufferCount = 1;
|
|
|
|
queue.submit(1, &submitInfo, waitFences[currentFrameBuffer]);
|
|
|
|
swapchain->queuePresent(queue, currentFrameBuffer, renderCompleteSemaphore);
|
|
}
|
|
|
|
void DrawTester::show()
|
|
{
|
|
window->show();
|
|
}
|
|
|
|
vk::RenderPass DrawTester::createRenderPass(vk::Format colorFormat)
|
|
{
|
|
std::vector<vk::AttachmentDescription> attachments(multisample ? 2 : 1);
|
|
|
|
if(multisample)
|
|
{
|
|
// Color attachment
|
|
attachments[0].format = colorFormat;
|
|
attachments[0].samples = vk::SampleCountFlagBits::e4;
|
|
attachments[0].loadOp = vk::AttachmentLoadOp::eClear;
|
|
attachments[0].storeOp = vk::AttachmentStoreOp::eStore;
|
|
attachments[0].stencilLoadOp = vk::AttachmentLoadOp::eDontCare;
|
|
attachments[0].stencilStoreOp = vk::AttachmentStoreOp::eDontCare;
|
|
attachments[0].initialLayout = vk::ImageLayout::eUndefined;
|
|
attachments[0].finalLayout = vk::ImageLayout::eColorAttachmentOptimal;
|
|
|
|
// Resolve attachment
|
|
attachments[1].format = colorFormat;
|
|
attachments[1].samples = vk::SampleCountFlagBits::e1;
|
|
attachments[1].loadOp = vk::AttachmentLoadOp::eDontCare;
|
|
attachments[1].storeOp = vk::AttachmentStoreOp::eStore;
|
|
attachments[1].stencilLoadOp = vk::AttachmentLoadOp::eDontCare;
|
|
attachments[1].stencilStoreOp = vk::AttachmentStoreOp::eDontCare;
|
|
attachments[1].initialLayout = vk::ImageLayout::eUndefined;
|
|
attachments[1].finalLayout = vk::ImageLayout::ePresentSrcKHR;
|
|
}
|
|
else
|
|
{
|
|
attachments[0].format = colorFormat;
|
|
attachments[0].samples = vk::SampleCountFlagBits::e1;
|
|
attachments[0].loadOp = vk::AttachmentLoadOp::eDontCare;
|
|
attachments[0].storeOp = vk::AttachmentStoreOp::eStore;
|
|
attachments[0].stencilLoadOp = vk::AttachmentLoadOp::eDontCare;
|
|
attachments[0].stencilStoreOp = vk::AttachmentStoreOp::eDontCare;
|
|
attachments[0].initialLayout = vk::ImageLayout::eUndefined;
|
|
attachments[0].finalLayout = vk::ImageLayout::ePresentSrcKHR;
|
|
}
|
|
|
|
vk::AttachmentReference attachment0;
|
|
attachment0.attachment = 0;
|
|
attachment0.layout = vk::ImageLayout::eColorAttachmentOptimal;
|
|
|
|
vk::AttachmentReference attachment1;
|
|
attachment1.attachment = 1;
|
|
attachment1.layout = vk::ImageLayout::eColorAttachmentOptimal;
|
|
|
|
vk::SubpassDescription subpassDescription;
|
|
subpassDescription.pipelineBindPoint = vk::PipelineBindPoint::eGraphics;
|
|
subpassDescription.colorAttachmentCount = 1;
|
|
subpassDescription.pResolveAttachments = multisample ? &attachment1 : nullptr;
|
|
subpassDescription.pColorAttachments = &attachment0;
|
|
|
|
std::array<vk::SubpassDependency, 2> dependencies;
|
|
|
|
dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;
|
|
dependencies[0].dstSubpass = 0;
|
|
dependencies[0].srcStageMask = vk::PipelineStageFlagBits::eBottomOfPipe;
|
|
dependencies[0].dstStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput;
|
|
dependencies[0].srcAccessMask = vk::AccessFlagBits::eMemoryRead;
|
|
dependencies[0].dstAccessMask = vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite;
|
|
dependencies[0].dependencyFlags = vk::DependencyFlagBits::eByRegion;
|
|
|
|
dependencies[1].srcSubpass = 0;
|
|
dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL;
|
|
dependencies[1].srcStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput;
|
|
dependencies[1].dstStageMask = vk::PipelineStageFlagBits::eBottomOfPipe;
|
|
dependencies[1].srcAccessMask = vk::AccessFlagBits::eColorAttachmentRead | vk::AccessFlagBits::eColorAttachmentWrite;
|
|
dependencies[1].dstAccessMask = vk::AccessFlagBits::eMemoryRead;
|
|
dependencies[1].dependencyFlags = vk::DependencyFlagBits::eByRegion;
|
|
|
|
vk::RenderPassCreateInfo renderPassInfo;
|
|
renderPassInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
|
|
renderPassInfo.pAttachments = attachments.data();
|
|
renderPassInfo.subpassCount = 1;
|
|
renderPassInfo.pSubpasses = &subpassDescription;
|
|
renderPassInfo.dependencyCount = static_cast<uint32_t>(dependencies.size());
|
|
renderPassInfo.pDependencies = dependencies.data();
|
|
|
|
return device.createRenderPass(renderPassInfo);
|
|
}
|
|
|
|
void DrawTester::createFramebuffers(vk::RenderPass renderPass)
|
|
{
|
|
framebuffers.resize(swapchain->imageCount());
|
|
|
|
for(size_t i = 0; i < framebuffers.size(); i++)
|
|
{
|
|
framebuffers[i].reset(new Framebuffer(device, physicalDevice, swapchain->getImageView(i), swapchain->colorFormat, renderPass, swapchain->getExtent(), multisample));
|
|
}
|
|
}
|
|
|
|
void DrawTester::prepareVertices()
|
|
{
|
|
hooks.createVertexBuffers(*this);
|
|
}
|
|
|
|
vk::Pipeline DrawTester::createGraphicsPipeline(vk::RenderPass renderPass)
|
|
{
|
|
auto setLayoutBindings = hooks.createDescriptorSetLayout(*this);
|
|
|
|
std::vector<vk::DescriptorSetLayout> setLayouts;
|
|
if(!setLayoutBindings.empty())
|
|
{
|
|
vk::DescriptorSetLayoutCreateInfo layoutInfo;
|
|
layoutInfo.bindingCount = static_cast<uint32_t>(setLayoutBindings.size());
|
|
layoutInfo.pBindings = setLayoutBindings.data();
|
|
descriptorSetLayout = device.createDescriptorSetLayout(layoutInfo);
|
|
|
|
setLayouts.push_back(descriptorSetLayout);
|
|
}
|
|
|
|
vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo;
|
|
pipelineLayoutCreateInfo.setLayoutCount = static_cast<uint32_t>(setLayouts.size());
|
|
pipelineLayoutCreateInfo.pSetLayouts = setLayouts.data();
|
|
pipelineLayout = device.createPipelineLayout(pipelineLayoutCreateInfo);
|
|
|
|
vk::GraphicsPipelineCreateInfo pipelineCreateInfo;
|
|
pipelineCreateInfo.layout = pipelineLayout;
|
|
pipelineCreateInfo.renderPass = renderPass;
|
|
|
|
vk::PipelineInputAssemblyStateCreateInfo inputAssemblyState;
|
|
inputAssemblyState.topology = vk::PrimitiveTopology::eTriangleList;
|
|
|
|
vk::PipelineRasterizationStateCreateInfo rasterizationState;
|
|
rasterizationState.depthClampEnable = VK_FALSE;
|
|
rasterizationState.rasterizerDiscardEnable = VK_FALSE;
|
|
rasterizationState.polygonMode = vk::PolygonMode::eFill;
|
|
rasterizationState.cullMode = vk::CullModeFlagBits::eNone;
|
|
rasterizationState.frontFace = vk::FrontFace::eCounterClockwise;
|
|
rasterizationState.depthBiasEnable = VK_FALSE;
|
|
rasterizationState.lineWidth = 1.0f;
|
|
|
|
vk::PipelineColorBlendAttachmentState blendAttachmentState;
|
|
blendAttachmentState.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA;
|
|
blendAttachmentState.blendEnable = VK_FALSE;
|
|
vk::PipelineColorBlendStateCreateInfo colorBlendState;
|
|
colorBlendState.attachmentCount = 1;
|
|
colorBlendState.pAttachments = &blendAttachmentState;
|
|
|
|
vk::PipelineViewportStateCreateInfo viewportState;
|
|
viewportState.viewportCount = 1;
|
|
viewportState.scissorCount = 1;
|
|
|
|
std::vector<vk::DynamicState> dynamicStateEnables;
|
|
dynamicStateEnables.push_back(vk::DynamicState::eViewport);
|
|
dynamicStateEnables.push_back(vk::DynamicState::eScissor);
|
|
vk::PipelineDynamicStateCreateInfo dynamicState = {};
|
|
dynamicState.pDynamicStates = dynamicStateEnables.data();
|
|
dynamicState.dynamicStateCount = static_cast<uint32_t>(dynamicStateEnables.size());
|
|
|
|
vk::PipelineDepthStencilStateCreateInfo depthStencilState;
|
|
depthStencilState.depthTestEnable = VK_FALSE;
|
|
depthStencilState.depthWriteEnable = VK_FALSE;
|
|
depthStencilState.depthCompareOp = vk::CompareOp::eLessOrEqual;
|
|
depthStencilState.depthBoundsTestEnable = VK_FALSE;
|
|
depthStencilState.back.failOp = vk::StencilOp::eKeep;
|
|
depthStencilState.back.passOp = vk::StencilOp::eKeep;
|
|
depthStencilState.back.compareOp = vk::CompareOp::eAlways;
|
|
depthStencilState.stencilTestEnable = VK_FALSE;
|
|
depthStencilState.front = depthStencilState.back;
|
|
|
|
vk::PipelineMultisampleStateCreateInfo multisampleState;
|
|
multisampleState.rasterizationSamples = multisample ? vk::SampleCountFlagBits::e4 : vk::SampleCountFlagBits::e1;
|
|
multisampleState.pSampleMask = nullptr;
|
|
|
|
vk::ShaderModule vertexModule = hooks.createVertexShader(*this);
|
|
vk::ShaderModule fragmentModule = hooks.createFragmentShader(*this);
|
|
|
|
assert(vertexModule); // TODO: if nullptr, use a default
|
|
assert(fragmentModule); // TODO: if nullptr, use a default
|
|
|
|
std::array<vk::PipelineShaderStageCreateInfo, 2> shaderStages;
|
|
|
|
shaderStages[0].module = vertexModule;
|
|
shaderStages[0].stage = vk::ShaderStageFlagBits::eVertex;
|
|
shaderStages[0].pName = "main";
|
|
|
|
shaderStages[1].module = fragmentModule;
|
|
shaderStages[1].stage = vk::ShaderStageFlagBits::eFragment;
|
|
shaderStages[1].pName = "main";
|
|
|
|
pipelineCreateInfo.stageCount = static_cast<uint32_t>(shaderStages.size());
|
|
pipelineCreateInfo.pStages = shaderStages.data();
|
|
pipelineCreateInfo.pVertexInputState = &vertices.inputState;
|
|
pipelineCreateInfo.pInputAssemblyState = &inputAssemblyState;
|
|
pipelineCreateInfo.pRasterizationState = &rasterizationState;
|
|
pipelineCreateInfo.pColorBlendState = &colorBlendState;
|
|
pipelineCreateInfo.pMultisampleState = &multisampleState;
|
|
pipelineCreateInfo.pViewportState = &viewportState;
|
|
pipelineCreateInfo.pDepthStencilState = &depthStencilState;
|
|
pipelineCreateInfo.renderPass = renderPass;
|
|
pipelineCreateInfo.pDynamicState = &dynamicState;
|
|
|
|
auto pipeline = device.createGraphicsPipeline(nullptr, pipelineCreateInfo).value;
|
|
|
|
device.destroyShaderModule(fragmentModule);
|
|
device.destroyShaderModule(vertexModule);
|
|
|
|
return pipeline;
|
|
}
|
|
|
|
void DrawTester::createSynchronizationPrimitives()
|
|
{
|
|
vk::SemaphoreCreateInfo semaphoreCreateInfo;
|
|
presentCompleteSemaphore = device.createSemaphore(semaphoreCreateInfo);
|
|
renderCompleteSemaphore = device.createSemaphore(semaphoreCreateInfo);
|
|
|
|
vk::FenceCreateInfo fenceCreateInfo;
|
|
fenceCreateInfo.flags = vk::FenceCreateFlagBits::eSignaled;
|
|
waitFences.resize(swapchain->imageCount());
|
|
for(auto &fence : waitFences)
|
|
{
|
|
fence = device.createFence(fenceCreateInfo);
|
|
}
|
|
}
|
|
|
|
void DrawTester::createCommandBuffers(vk::RenderPass renderPass)
|
|
{
|
|
vk::CommandPoolCreateInfo commandPoolCreateInfo;
|
|
commandPoolCreateInfo.queueFamilyIndex = queueFamilyIndex;
|
|
commandPoolCreateInfo.flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer;
|
|
commandPool = device.createCommandPool(commandPoolCreateInfo);
|
|
|
|
std::vector<vk::DescriptorSet> descriptorSets;
|
|
if(descriptorSetLayout)
|
|
{
|
|
std::array<vk::DescriptorPoolSize, 1> poolSizes = {};
|
|
poolSizes[0].type = vk::DescriptorType::eCombinedImageSampler;
|
|
poolSizes[0].descriptorCount = 1;
|
|
|
|
vk::DescriptorPoolCreateInfo poolInfo;
|
|
poolInfo.poolSizeCount = static_cast<uint32_t>(poolSizes.size());
|
|
poolInfo.pPoolSizes = poolSizes.data();
|
|
poolInfo.maxSets = 1;
|
|
|
|
descriptorPool = device.createDescriptorPool(poolInfo);
|
|
|
|
std::vector<vk::DescriptorSetLayout> layouts(1, descriptorSetLayout);
|
|
vk::DescriptorSetAllocateInfo allocInfo;
|
|
allocInfo.descriptorPool = descriptorPool;
|
|
allocInfo.descriptorSetCount = 1;
|
|
allocInfo.pSetLayouts = layouts.data();
|
|
|
|
descriptorSets = device.allocateDescriptorSets(allocInfo);
|
|
|
|
hooks.updateDescriptorSet(*this, commandPool, descriptorSets[0]);
|
|
}
|
|
|
|
vk::CommandBufferAllocateInfo commandBufferAllocateInfo;
|
|
commandBufferAllocateInfo.commandPool = commandPool;
|
|
commandBufferAllocateInfo.commandBufferCount = static_cast<uint32_t>(swapchain->imageCount());
|
|
commandBufferAllocateInfo.level = vk::CommandBufferLevel::ePrimary;
|
|
|
|
commandBuffers = device.allocateCommandBuffers(commandBufferAllocateInfo);
|
|
|
|
for(size_t i = 0; i < commandBuffers.size(); i++)
|
|
{
|
|
vk::CommandBufferBeginInfo commandBufferBeginInfo;
|
|
commandBuffers[i].begin(commandBufferBeginInfo);
|
|
|
|
vk::ClearValue clearValues[1];
|
|
clearValues[0].color = vk::ClearColorValue(std::array<float, 4>{ 0.5f, 0.5f, 0.5f, 1.0f });
|
|
|
|
vk::RenderPassBeginInfo renderPassBeginInfo;
|
|
renderPassBeginInfo.framebuffer = framebuffers[i]->getFramebuffer();
|
|
renderPassBeginInfo.renderPass = renderPass;
|
|
renderPassBeginInfo.renderArea.offset.x = 0;
|
|
renderPassBeginInfo.renderArea.offset.y = 0;
|
|
renderPassBeginInfo.renderArea.extent = windowSize;
|
|
renderPassBeginInfo.clearValueCount = ARRAY_SIZE(clearValues);
|
|
renderPassBeginInfo.pClearValues = clearValues;
|
|
commandBuffers[i].beginRenderPass(renderPassBeginInfo, vk::SubpassContents::eInline);
|
|
|
|
// Set dynamic state
|
|
vk::Viewport viewport(0.0f, 0.0f, static_cast<float>(windowSize.width), static_cast<float>(windowSize.height), 0.0f, 1.0f);
|
|
commandBuffers[i].setViewport(0, 1, &viewport);
|
|
|
|
vk::Rect2D scissor(vk::Offset2D(0, 0), windowSize);
|
|
commandBuffers[i].setScissor(0, 1, &scissor);
|
|
|
|
if(!descriptorSets.empty())
|
|
{
|
|
commandBuffers[i].bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipelineLayout, 0, 1, &descriptorSets[0], 0, nullptr);
|
|
}
|
|
|
|
// Draw
|
|
if(vertices.numVertices > 0)
|
|
{
|
|
commandBuffers[i].bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline);
|
|
VULKAN_HPP_NAMESPACE::DeviceSize offset = 0;
|
|
commandBuffers[i].bindVertexBuffers(0, 1, &vertices.buffer, &offset);
|
|
commandBuffers[i].draw(vertices.numVertices, 1, 0, 0);
|
|
}
|
|
|
|
commandBuffers[i].endRenderPass();
|
|
commandBuffers[i].end();
|
|
}
|
|
}
|
|
|
|
void DrawTester::addVertexBuffer(void *vertexBufferData, size_t vertexBufferDataSize, size_t vertexSize, std::vector<vk::VertexInputAttributeDescription> inputAttributes)
|
|
{
|
|
assert(!vertices.buffer); // For now, only support adding once
|
|
|
|
vk::BufferCreateInfo vertexBufferInfo;
|
|
vertexBufferInfo.size = vertexBufferDataSize;
|
|
vertexBufferInfo.usage = vk::BufferUsageFlagBits::eVertexBuffer;
|
|
vertices.buffer = device.createBuffer(vertexBufferInfo);
|
|
|
|
vk::MemoryAllocateInfo memoryAllocateInfo;
|
|
vk::MemoryRequirements memoryRequirements = device.getBufferMemoryRequirements(vertices.buffer);
|
|
memoryAllocateInfo.allocationSize = memoryRequirements.size;
|
|
memoryAllocateInfo.memoryTypeIndex = Util::getMemoryTypeIndex(physicalDevice, memoryRequirements.memoryTypeBits, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent);
|
|
vertices.memory = device.allocateMemory(memoryAllocateInfo);
|
|
|
|
void *data = device.mapMemory(vertices.memory, 0, VK_WHOLE_SIZE);
|
|
memcpy(data, vertexBufferData, vertexBufferDataSize);
|
|
device.unmapMemory(vertices.memory);
|
|
device.bindBufferMemory(vertices.buffer, vertices.memory, 0);
|
|
|
|
vertices.inputBinding.binding = 0;
|
|
vertices.inputBinding.stride = static_cast<uint32_t>(vertexSize);
|
|
vertices.inputBinding.inputRate = vk::VertexInputRate::eVertex;
|
|
|
|
vertices.inputAttributes = std::move(inputAttributes);
|
|
|
|
vertices.inputState.vertexBindingDescriptionCount = 1;
|
|
vertices.inputState.pVertexBindingDescriptions = &vertices.inputBinding;
|
|
vertices.inputState.vertexAttributeDescriptionCount = static_cast<uint32_t>(vertices.inputAttributes.size());
|
|
vertices.inputState.pVertexAttributeDescriptions = vertices.inputAttributes.data();
|
|
|
|
// Note that we assume data is tightly packed
|
|
vertices.numVertices = static_cast<uint32_t>(vertexBufferDataSize / vertexSize);
|
|
}
|
|
|
|
vk::ShaderModule DrawTester::createShaderModule(const char *glslSource, EShLanguage glslLanguage)
|
|
{
|
|
auto spirv = Util::compileGLSLtoSPIRV(glslSource, glslLanguage);
|
|
|
|
vk::ShaderModuleCreateInfo moduleCreateInfo;
|
|
moduleCreateInfo.codeSize = spirv.size() * sizeof(uint32_t);
|
|
moduleCreateInfo.pCode = (uint32_t *)spirv.data();
|
|
|
|
return device.createShaderModule(moduleCreateInfo);
|
|
}
|