Add ktx
This commit is contained in:
@@ -0,0 +1,769 @@
|
||||
/* -*- 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;
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/* -*- tab-width: 4; -*- */
|
||||
/* vi: set sw=2 ts=4 expandtab: */
|
||||
|
||||
/*
|
||||
* Copyright 2017-2020 Mark Callow.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef _INSTANCE_SAMPLE_BASE_H_
|
||||
#define _INSTANCE_SAMPLE_BASE_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "VulkanLoadTestSample.h"
|
||||
|
||||
#include <ktxvulkan.h>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
class InstancedSampleBase : public VulkanLoadTestSample
|
||||
{
|
||||
public:
|
||||
InstancedSampleBase(VulkanContext& vkctx,
|
||||
uint32_t width, uint32_t height,
|
||||
const char* const szArgs,
|
||||
const std::string sBasePath);
|
||||
virtual ~InstancedSampleBase();
|
||||
|
||||
virtual void resize(uint32_t width, uint32_t height);
|
||||
virtual void run(uint32_t msTicks);
|
||||
|
||||
//virtual void getOverlayText(VulkanTextOverlay *textOverlay, float yOffset);
|
||||
virtual const char* customizeTitle(const char* const title);
|
||||
|
||||
protected:
|
||||
ktxVulkanTexture texture;
|
||||
vk::Sampler sampler;
|
||||
vk::ImageView imageView;
|
||||
vk::ImageTiling tiling;
|
||||
vk::Filter filter;
|
||||
|
||||
uint32_t instanceCount;
|
||||
|
||||
bool transcoded;
|
||||
vk::Format transcodedFormat;
|
||||
std::string title;
|
||||
|
||||
struct {
|
||||
vk::PipelineVertexInputStateCreateInfo inputState;
|
||||
std::vector<vk::VertexInputBindingDescription> bindingDescriptions;
|
||||
std::vector<vk::VertexInputAttributeDescription> attributeDescriptions;
|
||||
} vertices;
|
||||
|
||||
MeshBuffer quad;
|
||||
|
||||
UniformData uniformDataVS;
|
||||
|
||||
struct UboInstanceData {
|
||||
// Model matrix
|
||||
glm::mat4 model;
|
||||
};
|
||||
|
||||
struct {
|
||||
// Global matrices
|
||||
struct {
|
||||
glm::mat4 projection;
|
||||
glm::mat4 view;
|
||||
} matrices;
|
||||
// N.B. The UBO structure declared in the shader has the array of
|
||||
// instance data inside the structure rather than pointed at from the
|
||||
// structure. The start of the array will be aligned on a 16-byte
|
||||
// boundary as it starts with a matrix.
|
||||
//
|
||||
// Separate data for each instance
|
||||
UboInstanceData *instance;
|
||||
} uboVS;
|
||||
|
||||
struct {
|
||||
vk::Pipeline solid;
|
||||
} pipelines;
|
||||
|
||||
vk::PipelineLayout pipelineLayout;
|
||||
vk::DescriptorSet descriptorSet;
|
||||
vk::DescriptorSetLayout descriptorSetLayout;
|
||||
vk::DescriptorPool descriptorPool;
|
||||
|
||||
void cleanup();
|
||||
|
||||
void buildCommandBuffers();
|
||||
|
||||
// Setup vertices for a single uv-mapped quad
|
||||
void generateQuad();
|
||||
|
||||
using DescriptorBindings = std::vector<vk::DescriptorSetLayoutBinding>;
|
||||
using PushConstantRanges = std::vector<vk::PushConstantRange>;
|
||||
virtual void addSubclassDescriptors(DescriptorBindings&) { }
|
||||
virtual void addSubclassPushConstantRanges(PushConstantRanges&) { }
|
||||
virtual void setSubclassPushConstants(uint32_t) { }
|
||||
void setupVertexDescriptions();
|
||||
void setupDescriptorPool();
|
||||
void setupDescriptorSetLayout();
|
||||
void setupDescriptorSet();
|
||||
void preparePipelines(const char* const fragShaderName,
|
||||
const char* const vertShaderName,
|
||||
uint32_t instanceCountConstId);
|
||||
|
||||
void prepareUniformBuffers(// See note in prepare declaration.
|
||||
uint32_t shaderDeclaredInstances);
|
||||
void updateUniformBufferMatrices();
|
||||
|
||||
void prepareSamplerAndView();
|
||||
|
||||
void prepare(const char* const fragShaderName,
|
||||
const char* const vertShaderName,
|
||||
uint32_t instanceCountConstId,
|
||||
uint32_t instanceCount,
|
||||
// Solely because of MoltenVK issue #1420.
|
||||
// It can't specialize array length constants.
|
||||
uint32_t shaderDeclaredInstances);
|
||||
|
||||
void processArgs(std::string sArgs);
|
||||
|
||||
virtual void viewChanged()
|
||||
{
|
||||
updateUniformBufferMatrices();
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _INSTANCE_SAMPLE_BASE_H_ */
|
||||
@@ -0,0 +1,818 @@
|
||||
/* -*- tab-width: 4; -*- */
|
||||
/* vi: set sw=2 ts=4 expandtab : */
|
||||
|
||||
/*
|
||||
* Copyright 2017-2020 Mark Callow.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @class Texture
|
||||
* @~English
|
||||
*
|
||||
* @brief Test loading of 2D textures.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define _CRT_SECURE_NO_WARNINGS // For sscanf
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <exception>
|
||||
#include <vector>
|
||||
|
||||
#include "argparser.h"
|
||||
#include "Texture.h"
|
||||
#include "VulkanTextureTranscoder.hpp"
|
||||
#include "ltexceptions.h"
|
||||
|
||||
#define VERTEX_BUFFER_BIND_ID 0
|
||||
#define ENABLE_VALIDATION false
|
||||
|
||||
// Vertex layout for this example
|
||||
struct Vertex {
|
||||
std::array<float, 3> pos;
|
||||
std::array<float, 2> uv;
|
||||
std::array<float, 3> normal;
|
||||
std::array<float, 3> color;
|
||||
};
|
||||
|
||||
VulkanLoadTestSample*
|
||||
Texture::create(VulkanContext& vkctx,
|
||||
uint32_t width, uint32_t height,
|
||||
const char* const szArgs,
|
||||
const std::string sBasePath)
|
||||
{
|
||||
return new Texture(vkctx, width, height, szArgs, sBasePath);
|
||||
}
|
||||
|
||||
Texture::Texture(VulkanContext& vkctx,
|
||||
uint32_t width, uint32_t height,
|
||||
const char* const szArgs,
|
||||
const std::string sBasePath)
|
||||
: VulkanLoadTestSample(vkctx, width, height, sBasePath)
|
||||
{
|
||||
zoom = -2.5f;
|
||||
rotation = { 0.0f, 15.0f, 0.0f };
|
||||
tiling = vk::ImageTiling::eOptimal;
|
||||
useSubAlloc = UseSuballocator::No;
|
||||
rgbcolor upperLeftColor{ 0.7f, 0.1f, 0.2f };
|
||||
rgbcolor lowerLeftColor{ 0.8f, 0.9f, 0.3f };
|
||||
rgbcolor upperRightColor{ 0.4f, 1.0f, 0.5f };
|
||||
rgbcolor lowerRightColor{ 0.0f, 0.6f, 0.1f };
|
||||
|
||||
transcoded = false;
|
||||
|
||||
quadColor = { upperLeftColor, lowerLeftColor,
|
||||
upperRightColor, lowerRightColor };
|
||||
|
||||
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::FormatProperties properties;
|
||||
vkctx.gpu.getFormatProperties(vkFormat, &properties);
|
||||
vk::FormatFeatureFlags& features = tiling == vk::ImageTiling::eLinear ?
|
||||
properties.linearTilingFeatures :
|
||||
properties.optimalTilingFeatures;
|
||||
vk::FormatFeatureFlags wantedFeatures =
|
||||
vk::FormatFeatureFlagBits::eSampledImage
|
||||
| vk::FormatFeatureFlagBits::eSampledImageFilterLinear;
|
||||
if (!(features & wantedFeatures)) {
|
||||
ktxTexture_Destroy(kTexture);
|
||||
throw unsupported_ttype();
|
||||
}
|
||||
|
||||
if (useSubAlloc == UseSuballocator::Yes)
|
||||
{
|
||||
VkInstance vkInst = vkctx.instance;
|
||||
VMA_CALLBACKS::InitVMA(vdi.physicalDevice, vdi.device, vkInst, vdi.deviceMemoryProperties);
|
||||
|
||||
ktxresult = ktxTexture_VkUploadEx_WithSuballocator(kTexture, &vdi, &texture,
|
||||
static_cast<VkImageTiling>(tiling),
|
||||
VK_IMAGE_USAGE_SAMPLED_BIT,
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, &subAllocatorCallbacks);
|
||||
}
|
||||
else // Keep separate call so ktxTexture_VkUploadEx is also tested.
|
||||
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());
|
||||
}
|
||||
|
||||
if (kTexture->orientation.x == KTX_ORIENT_X_LEFT)
|
||||
sign_s = -1;
|
||||
if (kTexture->orientation.y == KTX_ORIENT_Y_UP)
|
||||
sign_t = -1;
|
||||
|
||||
ktx_uint32_t swizzleLen;
|
||||
char* swizzleStr;
|
||||
ktxresult = ktxHashList_FindValue(&kTexture->kvDataHead, KTX_SWIZZLE_KEY,
|
||||
&swizzleLen, (void**)&swizzleStr);
|
||||
if (ktxresult == KTX_SUCCESS && swizzleLen == 5) {
|
||||
if (gpuSupportsSwizzle()) {
|
||||
swizzle.r = swizzleStr[0] == 'r' ? vk::ComponentSwizzle::eR
|
||||
: swizzleStr[0] == 'g' ? vk::ComponentSwizzle::eG
|
||||
: swizzleStr[0] == 'b' ? vk::ComponentSwizzle::eB
|
||||
: swizzleStr[0] == 'a' ? vk::ComponentSwizzle::eA
|
||||
: swizzleStr[0] == '0' ? vk::ComponentSwizzle::eZero
|
||||
: vk::ComponentSwizzle::eOne;
|
||||
swizzle.g = swizzleStr[1] == 'r' ? vk::ComponentSwizzle::eR
|
||||
: swizzleStr[1] == 'g' ? vk::ComponentSwizzle::eG
|
||||
: swizzleStr[1] == 'b' ? vk::ComponentSwizzle::eB
|
||||
: swizzleStr[1] == 'a' ? vk::ComponentSwizzle::eA
|
||||
: swizzleStr[1] == '0' ? vk::ComponentSwizzle::eZero
|
||||
: vk::ComponentSwizzle::eOne;
|
||||
swizzle.b = swizzleStr[2] == 'r' ? vk::ComponentSwizzle::eR
|
||||
: swizzleStr[2] == 'g' ? vk::ComponentSwizzle::eG
|
||||
: swizzleStr[2] == 'b' ? vk::ComponentSwizzle::eB
|
||||
: swizzleStr[2] == 'a' ? vk::ComponentSwizzle::eA
|
||||
: swizzleStr[2] == '0' ? vk::ComponentSwizzle::eZero
|
||||
: vk::ComponentSwizzle::eOne;
|
||||
swizzle.a = swizzleStr[3] == 'r' ? vk::ComponentSwizzle::eR
|
||||
: swizzleStr[3] == 'g' ? vk::ComponentSwizzle::eG
|
||||
: swizzleStr[3] == 'b' ? vk::ComponentSwizzle::eB
|
||||
: swizzleStr[3] == 'a' ? vk::ComponentSwizzle::eA
|
||||
: swizzleStr[3] == '0' ? vk::ComponentSwizzle::eZero
|
||||
: vk::ComponentSwizzle::eOne;
|
||||
} else {
|
||||
std::stringstream message;
|
||||
message << "Input file has swizzle metadata but "
|
||||
<< "app is running on a VK_KHR_portability_subset device "
|
||||
<< "that does not support swizzling.";
|
||||
// I have absolutely no idea why the following line makes clang
|
||||
// raise an error about no matching conversion from
|
||||
// std::__1::basic_stringstream to unsupported_ttype
|
||||
// so I've resorted to using a temporary variable.
|
||||
//throw(unsupported_ttype(message.str());
|
||||
std::string msg = message.str();
|
||||
throw(unsupported_ttype(msg));
|
||||
}
|
||||
}
|
||||
|
||||
ktxTexture_Destroy(kTexture);
|
||||
ktxVulkanDeviceInfo_Destruct(&vdi);
|
||||
|
||||
try {
|
||||
prepare();
|
||||
} catch (std::exception& e) {
|
||||
(void)e; // To quiet unused variable warnings from some compilers.
|
||||
cleanup();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
Texture::~Texture()
|
||||
{
|
||||
cleanup();
|
||||
}
|
||||
|
||||
void
|
||||
Texture::resize(uint32_t width, uint32_t height)
|
||||
{
|
||||
this->w_width = width;
|
||||
this->w_height = height;
|
||||
vkctx.destroyDrawCommandBuffers();
|
||||
vkctx.createDrawCommandBuffers();
|
||||
buildCommandBuffers();
|
||||
updateUniformBuffers();
|
||||
}
|
||||
|
||||
void
|
||||
Texture::run(uint32_t /*msTicks*/)
|
||||
{
|
||||
// Nothing to do since the scene is not animated.
|
||||
// VulkanLoadTests base class redraws from the command buffer we built.
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
|
||||
void
|
||||
Texture::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},
|
||||
{"use-vma", argparser::option::no_argument, (int*)&useSubAlloc, (int)UseSuballocator::Yes},
|
||||
{"qcolor", argparser::option::required_argument, NULL, 1},
|
||||
{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;
|
||||
case 1:
|
||||
{
|
||||
std::istringstream in(ap.optarg);
|
||||
rgbcolor clr;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 4 && !in.eof(); i++) {
|
||||
in >> clr[0] >> skip(",") >> clr[1] >> skip(",") >> clr[2];
|
||||
quadColor[i] = clr;
|
||||
if (!in.eof())
|
||||
in >> skip(",");
|
||||
}
|
||||
assert(!in.fail() && (i == 1 || i == 4));
|
||||
if (i == 1) {
|
||||
for(; i < 4; i++)
|
||||
quadColor[i] = quadColor[0];
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: assert(false); // Error in args in sample table.
|
||||
}
|
||||
}
|
||||
assert(ap.optind < argv.size());
|
||||
ktxfilename = argv[ap.optind];
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
// It is difficult to have these members clean up up their own mess, hence
|
||||
// this. Some of them are vulkan.hpp objects that have no destructors and
|
||||
// no record of the device. We could add destructors for our own but each
|
||||
// would have to remember the device.
|
||||
void
|
||||
Texture::cleanup()
|
||||
{
|
||||
// Clean up used Vulkan resources
|
||||
|
||||
vkctx.destroyDrawCommandBuffers();
|
||||
|
||||
if (sampler)
|
||||
vkctx.device.destroySampler(sampler);
|
||||
if (imageView)
|
||||
vkctx.device.destroyImageView(imageView);
|
||||
|
||||
|
||||
if (useSubAlloc == UseSuballocator::Yes)
|
||||
{
|
||||
VkDevice vkDev = vkctx.device;
|
||||
(void)ktxVulkanTexture_Destruct_WithSuballocator(&texture, vkDev, VK_NULL_HANDLE, &subAllocatorCallbacks);
|
||||
VMA_CALLBACKS::DestroyVMA();
|
||||
}
|
||||
else // Keep separate call so ktxVulkanTexture_Destruct is also tested.
|
||||
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);
|
||||
|
||||
quad.freeResources(vkctx.device);
|
||||
uniformDataVS.freeResources(vkctx.device);
|
||||
}
|
||||
|
||||
void
|
||||
Texture::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));
|
||||
|
||||
vkCmdBindDescriptorSets(vkctx.drawCmdBuffers[i],
|
||||
VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1,
|
||||
&static_cast<const VkDescriptorSet&>(descriptorSet), 0, NULL);
|
||||
vkCmdBindPipeline(vkctx.drawCmdBuffers[i],
|
||||
VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.solid);
|
||||
|
||||
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);
|
||||
|
||||
vkCmdDrawIndexed(vkctx.drawCmdBuffers[i], quad.indexCount, 1, 0, 0, 0);
|
||||
|
||||
vkCmdEndRenderPass(vkctx.drawCmdBuffers[i]);
|
||||
|
||||
VK_CHECK_RESULT(vkEndCommandBuffer(vkctx.drawCmdBuffers[i]));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Texture::generateQuad()
|
||||
{
|
||||
// Setup vertices for a single uv-mapped quad
|
||||
#define DIM 1.0f
|
||||
#define NORMAL { 0.0f, 0.0f, 1.0f }
|
||||
std::vector<Vertex> vertexBuffer = {
|
||||
{ { -DIM, -DIM, 0.0f }, { 0.0f, 0.0f }, NORMAL, { quadColor[0] } },
|
||||
{ { -DIM, DIM, 0.0f }, { 0.0f, 1.0f }, NORMAL, { quadColor[1] } },
|
||||
{ { DIM, -DIM, 0.0f }, { 1.0f, 0.0f }, NORMAL, { quadColor[2] } },
|
||||
{ { DIM, DIM, 0.0f }, { 1.0f, 1.0f }, NORMAL, { quadColor[3] } }
|
||||
};
|
||||
#undef DIM
|
||||
#undef NORMAL
|
||||
|
||||
if (sign_s < 0 || sign_t < 0) {
|
||||
// Transform the texture coordinates to get correct image orientation.
|
||||
for (uint32_t i = 0; i < vertexBuffer.size(); i++) {
|
||||
if (sign_t < 1) {
|
||||
vertexBuffer[i].uv[1] = vertexBuffer[i].uv[1] * -1 + 1;
|
||||
}
|
||||
if (sign_s < 1) {
|
||||
vertexBuffer[i].uv[0] = vertexBuffer[i].uv[0] * -1 + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
vkctx.createBuffer(
|
||||
vk::BufferUsageFlagBits::eVertexBuffer,
|
||||
vertexBuffer.size() * sizeof(Vertex),
|
||||
vertexBuffer.data(),
|
||||
&quad.vertices.buf,
|
||||
&quad.vertices.mem);
|
||||
|
||||
// Setup indices
|
||||
std::vector<uint32_t> indexBuffer = { 0,1,2,3 };
|
||||
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
|
||||
Texture::setupVertexDescriptions()
|
||||
{
|
||||
// Binding description
|
||||
vertices.bindingDescriptions.resize(1);
|
||||
vertices.bindingDescriptions[0] =
|
||||
vk::VertexInputBindingDescription(
|
||||
VERTEX_BUFFER_BIND_ID,
|
||||
sizeof(Vertex),
|
||||
vk::VertexInputRate::eVertex);
|
||||
//#define OFFSET(f) (&(((struct Vertex*)0)->f) - &(struct Vertex*)0)
|
||||
// Attribute descriptions
|
||||
// Describes memory layout and shader positions
|
||||
vertices.attributeDescriptions.resize(4);
|
||||
// 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);
|
||||
// Location 2 : Vertex normal
|
||||
vertices.attributeDescriptions[2] =
|
||||
vk::VertexInputAttributeDescription(
|
||||
2,
|
||||
VERTEX_BUFFER_BIND_ID,
|
||||
vk::Format::eR32G32B32Sfloat,
|
||||
sizeof(float) * 5);
|
||||
// Location 3 : Color
|
||||
vertices.attributeDescriptions[3] =
|
||||
vk::VertexInputAttributeDescription(
|
||||
3,
|
||||
VERTEX_BUFFER_BIND_ID,
|
||||
vk::Format::eR32G32B32Sfloat,
|
||||
sizeof(float) * 8);
|
||||
|
||||
|
||||
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
|
||||
Texture::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
|
||||
Texture::setupDescriptorSetLayout()
|
||||
{
|
||||
std::vector<vk::DescriptorSetLayoutBinding> setLayoutBindings =
|
||||
{
|
||||
// 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},
|
||||
};
|
||||
|
||||
vk::DescriptorSetLayoutCreateInfo descriptorLayout(
|
||||
{},
|
||||
static_cast<uint32_t>(setLayoutBindings.size()),
|
||||
setLayoutBindings.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);
|
||||
|
||||
res = vkctx.device.createPipelineLayout(&pipelineLayoutCreateInfo,
|
||||
nullptr,
|
||||
&pipelineLayout);
|
||||
if (res != vk::Result::eSuccess) {
|
||||
throw bad_vulkan_alloc((int)res, "createPipelineLayout");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Texture::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
|
||||
Texture::preparePipelines()
|
||||
{
|
||||
vk::PipelineInputAssemblyStateCreateInfo inputAssemblyState(
|
||||
{},
|
||||
vk::PrimitiveTopology::eTriangleStrip,
|
||||
// primmitiveRestartEnable not needed but disabling it results in a MoltenVK
|
||||
// feature not present warning.
|
||||
true);
|
||||
|
||||
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();
|
||||
shaderStages[0] = loadShader(filepath + "texture.vert.spv",
|
||||
vk::ShaderStageFlagBits::eVertex);
|
||||
std::string fragShader = filepath;
|
||||
if (texture.viewType == VK_IMAGE_VIEW_TYPE_1D)
|
||||
fragShader += "texture1d";
|
||||
else
|
||||
fragShader += "texture2d";
|
||||
fragShader += ".frag.spv";
|
||||
shaderStages[1] = loadShader(fragShader,
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare and initialize uniform buffer containing shader uniforms
|
||||
void
|
||||
Texture::prepareUniformBuffers()
|
||||
{
|
||||
// Vertex shader uniform buffer block
|
||||
vkctx.createBuffer(
|
||||
vk::BufferUsageFlagBits::eUniformBuffer,
|
||||
sizeof(uboVS),
|
||||
&uboVS,
|
||||
&uniformDataVS.buffer,
|
||||
&uniformDataVS.memory,
|
||||
&uniformDataVS.descriptor);
|
||||
|
||||
updateUniformBuffers();
|
||||
}
|
||||
|
||||
void
|
||||
Texture::updateUniformBuffers()
|
||||
{
|
||||
if (w_width == 0 || w_height == 0)
|
||||
return;
|
||||
// Vertex shader
|
||||
uboVS.projection = glm::perspective(glm::radians(60.0f), (float)w_width / (float)w_height, 0.001f, 256.0f);
|
||||
glm::mat4 viewMatrix = glm::translate(glm::mat4(), glm::vec3(0.0f, 0.0f, zoom));
|
||||
|
||||
uboVS.model = viewMatrix * glm::translate(glm::mat4(), cameraPos);
|
||||
uboVS.model = glm::rotate(uboVS.model, glm::radians(rotation.x), glm::vec3(1.0f, 0.0f, 0.0f));
|
||||
uboVS.model = glm::rotate(uboVS.model, glm::radians(rotation.y), glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
uboVS.model = glm::rotate(uboVS.model, glm::radians(rotation.z), glm::vec3(0.0f, 0.0f, 1.0f));
|
||||
// Because MetalSL does not have a matrix inverse function...
|
||||
// It looks like the glm::mat3(glm::mat4) does something different than
|
||||
// GLSL. If I convert to mat3 here, only half the quad is lit. Do it in
|
||||
// the shader.
|
||||
uboVS.normal = inverse(transpose(uboVS.model));
|
||||
|
||||
uboVS.viewPos = glm::vec4(0.0f, 0.0f, -zoom, 0.0f);
|
||||
|
||||
uint8_t *pData;
|
||||
VK_CHECK_RESULT(vkMapMemory(vkctx.device, uniformDataVS.memory, 0, sizeof(uboVS), 0, (void **)&pData));
|
||||
memcpy(pData, &uboVS, sizeof(uboVS));
|
||||
vkUnmapMemory(vkctx.device, uniformDataVS.memory);
|
||||
}
|
||||
|
||||
void
|
||||
Texture::prepareSamplerAndView()
|
||||
{
|
||||
// Create sampler.
|
||||
vk::SamplerCreateInfo samplerInfo;
|
||||
// Set the non-default values
|
||||
samplerInfo.magFilter = vk::Filter::eLinear;
|
||||
samplerInfo.minFilter = vk::Filter::eLinear;
|
||||
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;
|
||||
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.components = swizzle;
|
||||
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
|
||||
Texture::prepare()
|
||||
{
|
||||
prepareSamplerAndView();
|
||||
generateQuad();
|
||||
setupVertexDescriptions();
|
||||
prepareUniformBuffers();
|
||||
setupDescriptorSetLayout();
|
||||
preparePipelines();
|
||||
setupDescriptorPool();
|
||||
setupDescriptorSet();
|
||||
vkctx.createDrawCommandBuffers();
|
||||
buildCommandBuffers();
|
||||
}
|
||||
|
||||
void
|
||||
Texture::changeLodBias(float delta)
|
||||
{
|
||||
uboVS.lodBias += delta;
|
||||
if (uboVS.lodBias < 0.0f)
|
||||
{
|
||||
uboVS.lodBias = 0.0f;
|
||||
}
|
||||
if (uboVS.lodBias > texture.levelCount)
|
||||
{
|
||||
uboVS.lodBias = (float)texture.levelCount;
|
||||
}
|
||||
updateUniformBuffers();
|
||||
//updateTextOverlay();
|
||||
}
|
||||
|
||||
void
|
||||
Texture::keyPressed(uint32_t keyCode)
|
||||
{
|
||||
switch (keyCode)
|
||||
{
|
||||
case SDLK_KP_PLUS:
|
||||
changeLodBias(0.1f);
|
||||
break;
|
||||
case SDLK_KP_MINUS:
|
||||
changeLodBias(-0.1f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Texture::getOverlayText(VulkanTextOverlay *textOverlay, float yOffset)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << std::setprecision(2) << std::fixed << uboVS.lodBias;
|
||||
textOverlay->addText("LOD bias: " + ss.str() + " (numpad +/- to change)",
|
||||
5.0f, yOffset, VulkanTextOverlay::alignLeft);
|
||||
}
|
||||
|
||||
const char*
|
||||
Texture::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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
/* -*- tab-width: 4; -*- */
|
||||
/* vi: set sw=2 ts=4 expandtab: */
|
||||
|
||||
/*
|
||||
* Copyright 2017-2020 Mark Callow.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "VulkanLoadTestSample.h"
|
||||
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
class Texture : public VulkanLoadTestSample
|
||||
{
|
||||
public:
|
||||
Texture(VulkanContext& vkctx,
|
||||
uint32_t width, uint32_t height,
|
||||
const char* const szArgs,
|
||||
const std::string sBasePath);
|
||||
~Texture();
|
||||
|
||||
virtual void resize(uint32_t width, uint32_t height);
|
||||
virtual void run(uint32_t msTicks);
|
||||
|
||||
virtual void getOverlayText(VulkanTextOverlay *textOverlay, float yOffset);
|
||||
virtual const char* customizeTitle(const char* const title);
|
||||
|
||||
static VulkanLoadTestSample*
|
||||
create(VulkanContext& vkctx,
|
||||
uint32_t width, uint32_t height,
|
||||
const char* const szArgs, const std::string sBasePath);
|
||||
|
||||
protected:
|
||||
enum class UseSuballocator
|
||||
{
|
||||
No = 0,
|
||||
Yes
|
||||
};
|
||||
|
||||
std::string filename;
|
||||
ktxVulkanTexture texture;
|
||||
vk::Sampler sampler;
|
||||
vk::ImageView imageView;
|
||||
vk::ImageTiling tiling;
|
||||
UseSuballocator useSubAlloc;
|
||||
vk::ComponentMapping swizzle;
|
||||
|
||||
struct {
|
||||
vk::PipelineVertexInputStateCreateInfo inputState;
|
||||
std::vector<vk::VertexInputBindingDescription> bindingDescriptions;
|
||||
std::vector<vk::VertexInputAttributeDescription> attributeDescriptions;
|
||||
} vertices;
|
||||
|
||||
MeshBuffer quad;
|
||||
typedef std::array<float, 3> rgbcolor;
|
||||
std::array<rgbcolor,4> quadColor;
|
||||
|
||||
UniformData uniformDataVS;
|
||||
|
||||
struct {
|
||||
glm::mat4 projection;
|
||||
glm::mat4 model;
|
||||
glm::mat4 normal;
|
||||
glm::vec4 viewPos;
|
||||
float lodBias = 0.0f;
|
||||
} uboVS;
|
||||
|
||||
struct {
|
||||
vk::Pipeline solid;
|
||||
} pipelines;
|
||||
|
||||
vk::PipelineLayout pipelineLayout;
|
||||
vk::DescriptorSet descriptorSet;
|
||||
vk::DescriptorSetLayout descriptorSetLayout;
|
||||
vk::DescriptorPool descriptorPool;
|
||||
|
||||
int sign_s = 1;
|
||||
int sign_t = 1;
|
||||
|
||||
bool transcoded;
|
||||
vk::Format transcodedFormat;
|
||||
std::string title;
|
||||
|
||||
void cleanup();
|
||||
void buildCommandBuffers();
|
||||
void generateQuad();
|
||||
void setupVertexDescriptions();
|
||||
void setupDescriptorPool();
|
||||
void setupDescriptorSetLayout();
|
||||
void setupDescriptorSet();
|
||||
void preparePipelines();
|
||||
// Prepare and initialize uniform buffer containing shader uniforms
|
||||
void prepareUniformBuffers();
|
||||
void updateUniformBuffers();
|
||||
void prepareSamplerAndView();
|
||||
void prepare();
|
||||
|
||||
void processArgs(std::string sArgs);
|
||||
|
||||
virtual void keyPressed(uint32_t keyCode);
|
||||
virtual void viewChanged()
|
||||
{
|
||||
updateUniformBuffers();
|
||||
}
|
||||
|
||||
void changeLodBias(float delta);
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
/* -*- tab-width: 4; -*- */
|
||||
/* vi: set sw=2 ts=4 expandtab: */
|
||||
|
||||
/*
|
||||
* Copyright 2017-2020 Mark Callow.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @class Texture3d
|
||||
* @~English
|
||||
*
|
||||
* @brief Definition of test sample for loading and displaying the slices of a 3d texture.
|
||||
*
|
||||
* @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 "Texture3d.h"
|
||||
#include "ltexceptions.h"
|
||||
|
||||
VulkanLoadTestSample*
|
||||
Texture3d::create(VulkanContext& vkctx,
|
||||
uint32_t width, uint32_t height,
|
||||
const char* const szArgs, const std::string sBasePath)
|
||||
{
|
||||
return new Texture3d(vkctx, width, height, szArgs, sBasePath);
|
||||
}
|
||||
|
||||
#define INSTANCE_COUNT_CONST_ID 1
|
||||
#define INSTANCES_DECLARED_IN_SHADER 30
|
||||
|
||||
Texture3d::Texture3d(VulkanContext& vkctx,
|
||||
uint32_t width, uint32_t height,
|
||||
const char* const szArgs, const std::string sBasePath)
|
||||
: InstancedSampleBase(vkctx, width, height, szArgs, sBasePath)
|
||||
{
|
||||
zoom = -15.0f;
|
||||
|
||||
if (texture.depth == 1) {
|
||||
std::stringstream message;
|
||||
|
||||
message << "Texture3d requires a 3d texture.";
|
||||
throw std::runtime_error(message.str());
|
||||
}
|
||||
|
||||
try {
|
||||
prepare("instancing3d.frag.spv", "instancing3d.vert.spv",
|
||||
INSTANCE_COUNT_CONST_ID, texture.depth,
|
||||
INSTANCES_DECLARED_IN_SHADER);
|
||||
} catch (std::exception& e) {
|
||||
(void)e; // To quiet unused variable warnings from some compilers.
|
||||
//cleanup(); // See explanation in TextureMipmap.cpp
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
// Addition of extra uniform buffer for the instance count is to work around
|
||||
// MoltenVK issue #issue 1421:
|
||||
// https://github.com/KhronosGroup/MoltenVK/issues/1421.
|
||||
void
|
||||
Texture3d::addSubclassDescriptors(DescriptorBindings& descriptorBindings)
|
||||
{
|
||||
descriptorBindings.push_back(vk::DescriptorSetLayoutBinding(
|
||||
2, // Binding 2 : uniform buffer for instanceCount value.
|
||||
vk::DescriptorType::eUniformBuffer,
|
||||
1,
|
||||
vk::ShaderStageFlagBits::eVertex
|
||||
));
|
||||
}
|
||||
#endif
|
||||
|
||||
// Providing instanceCount via a push constant is a workaround for
|
||||
// MoltenVK issue #1421:
|
||||
// https://github.com/KhronosGroup/MoltenVK/issues/1421.
|
||||
void
|
||||
Texture3d::addSubclassPushConstantRanges(PushConstantRanges& ranges)
|
||||
{
|
||||
ranges.push_back(vk::PushConstantRange(
|
||||
vk::ShaderStageFlagBits::eVertex,
|
||||
0, // offset
|
||||
sizeof(instanceCount)
|
||||
));
|
||||
}
|
||||
|
||||
void
|
||||
Texture3d::setSubclassPushConstants(uint32_t bufferIndex)
|
||||
{
|
||||
vkCmdPushConstants(
|
||||
vkctx.drawCmdBuffers[bufferIndex],
|
||||
pipelineLayout,
|
||||
VK_SHADER_STAGE_VERTEX_BIT,
|
||||
0,
|
||||
sizeof(instanceCount),
|
||||
&instanceCount
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/* -*- tab-width: 4; -*- */
|
||||
/* vi: set sw=2 ts=4 expandtab: */
|
||||
|
||||
/*
|
||||
* Copyright 2017-2020 Mark Callow.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef _TEXTURE_3D_H_
|
||||
#define _TEXTURE_3D_H_
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @file
|
||||
* @~English
|
||||
*
|
||||
* @brief Declaration of test sample for loading and displaying the slices of a 3d texture..
|
||||
*
|
||||
* @author Mark Callow, github.com/MarkCallow.
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "InstancedSampleBase.h"
|
||||
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
class Texture3d : public InstancedSampleBase
|
||||
{
|
||||
public:
|
||||
Texture3d(VulkanContext& vkctx,
|
||||
uint32_t width, uint32_t height,
|
||||
const char* const szArgs,
|
||||
const std::string sBasePath);
|
||||
|
||||
static VulkanLoadTestSample*
|
||||
create(VulkanContext& vkctx,
|
||||
uint32_t width, uint32_t height,
|
||||
const char* const szArgs, const std::string sBasePath);
|
||||
|
||||
protected:
|
||||
virtual void addSubclassPushConstantRanges(PushConstantRanges&);
|
||||
virtual void setSubclassPushConstants(uint32_t bufferIndex);
|
||||
};
|
||||
|
||||
#endif /* _TEXTURE_3D_H_ */
|
||||
@@ -0,0 +1,71 @@
|
||||
/* -*- 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 Definition of test sample for loading and displaying the layers of a 2D array texture.
|
||||
*
|
||||
* @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 "TextureArray.h"
|
||||
#include "ltexceptions.h"
|
||||
|
||||
VulkanLoadTestSample*
|
||||
TextureArray::create(VulkanContext& vkctx,
|
||||
uint32_t width, uint32_t height,
|
||||
const char* const szArgs, const std::string sBasePath)
|
||||
{
|
||||
return new TextureArray(vkctx, width, height, szArgs, sBasePath);
|
||||
}
|
||||
|
||||
#define INSTANCE_COUNT_CONST_ID 1
|
||||
#define INSTANCES_DECLARED_IN_SHADER 30
|
||||
|
||||
TextureArray::TextureArray(VulkanContext& vkctx,
|
||||
uint32_t width, uint32_t height,
|
||||
const char* const szArgs, const std::string sBasePath)
|
||||
: InstancedSampleBase(vkctx, width, height, szArgs, sBasePath)
|
||||
{
|
||||
zoom = -15.0f;
|
||||
|
||||
|
||||
if (texture.layerCount == 1) {
|
||||
std::stringstream message;
|
||||
|
||||
message << "TextureArray requires an array texture.";
|
||||
throw std::runtime_error(message.str());
|
||||
}
|
||||
|
||||
try {
|
||||
prepare("instancing.frag.spv", "instancing.vert.spv",
|
||||
INSTANCE_COUNT_CONST_ID, texture.layerCount,
|
||||
INSTANCES_DECLARED_IN_SHADER);
|
||||
} catch (std::exception& e) {
|
||||
(void)e; // To quiet unused variable warnings from some compilers.
|
||||
//cleanup(); // See explanation in TextureMipmap.cpp
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/* -*- tab-width: 4; -*- */
|
||||
/* vi: set sw=2 ts=4 expandtab: */
|
||||
|
||||
/*
|
||||
* Copyright 2017-2020 Mark Callow.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef _TEXTURE_ARRAY_H_
|
||||
#define _TEXTURE_ARRAY_H_
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @file
|
||||
* @~English
|
||||
*
|
||||
* @brief Declaration of test sample for loading and displaying the layers of a 2D array texture.
|
||||
*
|
||||
* @author Mark Callow, github.com/MarkCallow.
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "InstancedSampleBase.h"
|
||||
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
class TextureArray : public InstancedSampleBase
|
||||
{
|
||||
public:
|
||||
TextureArray(VulkanContext& vkctx,
|
||||
uint32_t width, uint32_t height,
|
||||
const char* const szArgs,
|
||||
const std::string sBasePath);
|
||||
|
||||
static VulkanLoadTestSample*
|
||||
create(VulkanContext& vkctx,
|
||||
uint32_t width, uint32_t height,
|
||||
const char* const szArgs, const std::string sBasePath);
|
||||
};
|
||||
|
||||
#endif /* _TEXTURE_ARRAY_H_ */
|
||||
@@ -0,0 +1,871 @@
|
||||
/* -*- tab-width: 4; -*- */
|
||||
/* vi: set sw=2 ts=4 expandtab: */
|
||||
|
||||
/*
|
||||
* Copyright 2017-2020 Mark Callow.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @class TextureCubemap
|
||||
* @~English
|
||||
*
|
||||
* @brief Test loading of cubemap textures.
|
||||
*
|
||||
* @author Mark Callow, github.com/MarkCallow.
|
||||
*
|
||||
* @par Acknowledgement
|
||||
* Thanks to Sascha Willems' - www.saschawillems.de - for the concept,
|
||||
* the VulkanTextOverlay VulkanMeshLoader classes and the shaders used
|
||||
* by this test.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <vector>
|
||||
|
||||
#include "TextureCubemap.h"
|
||||
#include "VulkanTextureTranscoder.hpp"
|
||||
#include "SwipeDetector.h"
|
||||
#include "argparser.h"
|
||||
#include "ltexceptions.h"
|
||||
|
||||
#define VERTEX_BUFFER_BIND_ID 0
|
||||
#define ENABLE_VALIDATION false
|
||||
#define USE_GL_RH_NDC 0
|
||||
#define NORMALIZE_AXES 0
|
||||
|
||||
// Vertex layout for this example
|
||||
std::vector<vkMeshLoader::VertexLayout> vertexLayout =
|
||||
{
|
||||
vkMeshLoader::VERTEX_LAYOUT_POSITION,
|
||||
vkMeshLoader::VERTEX_LAYOUT_NORMAL,
|
||||
vkMeshLoader::VERTEX_LAYOUT_UV
|
||||
};
|
||||
|
||||
VulkanLoadTestSample*
|
||||
TextureCubemap::create(VulkanContext& vkctx,
|
||||
uint32_t width, uint32_t height,
|
||||
const char* const szArgs, const std::string sBasePath)
|
||||
{
|
||||
return new TextureCubemap(vkctx, width, height, szArgs, sBasePath,
|
||||
USE_GL_RH_NDC ? 1 : -1);
|
||||
}
|
||||
|
||||
TextureCubemap::TextureCubemap(VulkanContext& vkctx,
|
||||
uint32_t width, uint32_t height,
|
||||
const char* const szArgs, const std::string sBasePath,
|
||||
int32_t yflip)
|
||||
: VulkanLoadTestSample(vkctx, width, height, sBasePath, yflip)
|
||||
{
|
||||
zoom = -4.0f;
|
||||
rotationSpeed = 0.25f;
|
||||
rotation = { -7.25f, 120.0f, 0.0f };
|
||||
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(),
|
||||
preloadImages ? KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT
|
||||
: 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::FormatProperties properties;
|
||||
vkctx.gpu.getFormatProperties(vkFormat, &properties);
|
||||
vk::FormatFeatureFlags wantedFeatures =
|
||||
vk::FormatFeatureFlagBits::eSampledImage
|
||||
| vk::FormatFeatureFlagBits::eSampledImageFilterLinear;
|
||||
if (!(properties.optimalTilingFeatures & wantedFeatures)) {
|
||||
ktxTexture_Destroy(kTexture);
|
||||
throw unsupported_ttype();
|
||||
}
|
||||
|
||||
ktxresult = ktxTexture_VkUpload(kTexture, &vdi, &cubeMap);
|
||||
|
||||
if (KTX_SUCCESS != ktxresult) {
|
||||
std::stringstream message;
|
||||
|
||||
message << "ktxTexture_VkUpload failed: " << ktxErrorString(ktxresult);
|
||||
throw std::runtime_error(message.str());
|
||||
}
|
||||
|
||||
if (kTexture->orientation.y == KTX_ORIENT_Y_DOWN) {
|
||||
// Assume a KTX-compliant cubemap. That means the faces are in a
|
||||
// LH coord system with +y up. Multiply the cube's y and z by -1 to
|
||||
// put the +z face in front of the view and keep +y up. Alternatively
|
||||
// we could multiply the y and x coords by -1 to keep the +y up while
|
||||
// placing the +z face in the +z direction.
|
||||
#if !USE_GL_RH_NDC
|
||||
ubo.uvwTransform = glm::scale(glm::mat4(1.0f), glm::vec3(1, -1, -1));
|
||||
#else
|
||||
// Scale the skybox cube's z by -1 to convert it to LH coords
|
||||
// with the +z face in front of the view.
|
||||
ubo.uvwTransform = glm::scale(glm::mat4(1.0f), glm::vec3(1, 1, -1));
|
||||
#endif
|
||||
} else {
|
||||
std::stringstream message;
|
||||
|
||||
message << "Cubemap faces have unsupported KTXorientation value.";
|
||||
throw std::runtime_error(message.str());
|
||||
}
|
||||
|
||||
ktxTexture_Destroy(kTexture);
|
||||
ktxVulkanDeviceInfo_Destruct(&vdi);
|
||||
|
||||
try {
|
||||
prepare();
|
||||
} catch (std::exception& e) {
|
||||
(void)e; // To quiet unused variable warnings from some compilers.
|
||||
cleanup();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
TextureCubemap::~TextureCubemap()
|
||||
{
|
||||
cleanup();
|
||||
}
|
||||
|
||||
int
|
||||
TextureCubemap::doEvent(SDL_Event* event)
|
||||
{
|
||||
int result = 0;
|
||||
switch(event->type) {
|
||||
case SDL_EVENT_USER:
|
||||
if (event->user.code == SwipeDetector::swipeGesture) {
|
||||
SwipeDetector::Direction direction
|
||||
= SwipeDetector::pointerToDirection(event->user.data1);
|
||||
switch (direction) {
|
||||
case SwipeDetector::Direction::up:
|
||||
toggleObject(+1);
|
||||
break;
|
||||
case SwipeDetector::Direction::down:
|
||||
toggleObject(-1);
|
||||
break;
|
||||
default:
|
||||
result = 1;
|
||||
}
|
||||
} else {
|
||||
result = 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
result = 1;
|
||||
}
|
||||
if (result == 1)
|
||||
result = VulkanLoadTestSample::doEvent(event);
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
TextureCubemap::resize(uint32_t width, uint32_t height)
|
||||
{
|
||||
this->w_width = width;
|
||||
this->w_height = height;
|
||||
rebuildCommandBuffers();
|
||||
updateUniformBuffers();
|
||||
}
|
||||
|
||||
void
|
||||
TextureCubemap::run(uint32_t /*msTicks*/)
|
||||
{
|
||||
// Nothing to do since the scene is not animated.
|
||||
// VulkanLoadTests base class redraws from the command buffer we built.
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
|
||||
void
|
||||
TextureCubemap::processArgs(std::string sArgs)
|
||||
{
|
||||
// Options descriptor
|
||||
struct argparser::option longopts[] = {
|
||||
{"external", argparser::option::no_argument, &externalFile, 1},
|
||||
{"preload", argparser::option::no_argument, &preloadImages, 1},
|
||||
{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
|
||||
TextureCubemap::cleanup()
|
||||
{
|
||||
// Clean up used Vulkan resources
|
||||
|
||||
// Clean up texture resources
|
||||
if (sampler)
|
||||
vkctx.device.destroySampler(sampler);
|
||||
if (imageView)
|
||||
vkctx.device.destroyImageView(imageView);
|
||||
ktxVulkanTexture_Destruct(&cubeMap, vkctx.device, nullptr);
|
||||
|
||||
if (pipelines.reflect)
|
||||
vkctx.device.destroyPipeline(pipelines.reflect);
|
||||
if (pipelines.skybox)
|
||||
vkctx.device.destroyPipeline(pipelines.skybox);
|
||||
if (pipelineLayout)
|
||||
vkctx.device.destroyPipelineLayout(pipelineLayout);
|
||||
if (descriptorSetLayout)
|
||||
vkctx.device.destroyDescriptorSetLayout(descriptorSetLayout);
|
||||
|
||||
vkctx.destroyDrawCommandBuffers();
|
||||
|
||||
for (size_t i = 0; i < meshes.objects.size(); i++)
|
||||
{
|
||||
vkMeshLoader::freeMeshBufferResources(vkctx.device, &meshes.objects[i]);
|
||||
}
|
||||
vkMeshLoader::freeMeshBufferResources(vkctx.device, &meshes.skybox);
|
||||
|
||||
uniformData.object.freeResources(vkctx.device);
|
||||
uniformData.skybox.freeResources(vkctx.device);
|
||||
}
|
||||
|
||||
void
|
||||
TextureCubemap::rebuildCommandBuffers()
|
||||
{
|
||||
if (!vkctx.checkDrawCommandBuffers())
|
||||
{
|
||||
vkctx.destroyDrawCommandBuffers();
|
||||
vkctx.createDrawCommandBuffers();
|
||||
}
|
||||
buildCommandBuffers();
|
||||
}
|
||||
|
||||
void
|
||||
TextureCubemap::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);
|
||||
|
||||
#if !USE_GL_RH_NDC
|
||||
vk::Viewport viewport(0, 0,
|
||||
(float)w_width, (float)w_height,
|
||||
0.0f, 1.0f);
|
||||
#else
|
||||
// Make OpenGL style viewport
|
||||
vk::Viewport viewport(0, (float)w_height,
|
||||
(float)w_width, -(float)w_height,
|
||||
0.0f, 1.0f);
|
||||
#endif
|
||||
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));
|
||||
|
||||
VkDeviceSize offsets[1] = { 0 };
|
||||
|
||||
// Skybox
|
||||
if (displaySkybox)
|
||||
{
|
||||
vkCmdBindDescriptorSets(vkctx.drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
pipelineLayout, 0, 1,
|
||||
&static_cast<const VkDescriptorSet&>(descriptorSets.skybox), 0, NULL);
|
||||
vkCmdBindVertexBuffers(vkctx.drawCmdBuffers[i], VERTEX_BUFFER_BIND_ID, 1, &meshes.skybox.vertices.buf, offsets);
|
||||
vkCmdBindIndexBuffer(vkctx.drawCmdBuffers[i], meshes.skybox.indices.buf, 0, VK_INDEX_TYPE_UINT32);
|
||||
vkCmdBindPipeline(vkctx.drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.skybox);
|
||||
vkCmdDrawIndexed(vkctx.drawCmdBuffers[i], meshes.skybox.indexCount, 1, 0, 0, 0);
|
||||
}
|
||||
|
||||
// 3D object
|
||||
vkCmdBindDescriptorSets(vkctx.drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
pipelineLayout, 0, 1,
|
||||
&static_cast<const VkDescriptorSet&>(descriptorSets.object), 0, NULL);
|
||||
vkCmdBindVertexBuffers(vkctx.drawCmdBuffers[i], VERTEX_BUFFER_BIND_ID, 1, &meshes.objects[meshes.objectIndex].vertices.buf, offsets);
|
||||
vkCmdBindIndexBuffer(vkctx.drawCmdBuffers[i], meshes.objects[meshes.objectIndex].indices.buf, 0, VK_INDEX_TYPE_UINT32);
|
||||
vkCmdBindPipeline(vkctx.drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.reflect);
|
||||
vkCmdDrawIndexed(vkctx.drawCmdBuffers[i], meshes.objects[meshes.objectIndex].indexCount, 1, 0, 0, 0);
|
||||
|
||||
vkCmdEndRenderPass(vkctx.drawCmdBuffers[i]);
|
||||
|
||||
VK_CHECK_RESULT(vkEndCommandBuffer(vkctx.drawCmdBuffers[i]));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TextureCubemap::loadMeshes()
|
||||
{
|
||||
std::string filepath = getAssetPath();
|
||||
|
||||
// Skybox
|
||||
loadMesh(filepath + "cube.obj", &meshes.skybox, vertexLayout, 0.05f);
|
||||
// Objects
|
||||
meshes.objects.resize(3);
|
||||
loadMesh(filepath + "sphere.obj", &meshes.objects[0], vertexLayout, 0.05f);
|
||||
loadMesh(filepath + "teapot.dae", &meshes.objects[1], vertexLayout, 0.05f);
|
||||
loadMesh(filepath + "torusknot.obj", &meshes.objects[2], vertexLayout, 0.05f);
|
||||
}
|
||||
|
||||
void
|
||||
TextureCubemap::setupVertexDescriptions()
|
||||
{
|
||||
// Binding description
|
||||
vertices.bindingDescriptions.resize(1);
|
||||
vertices.bindingDescriptions[0] =
|
||||
vk::VertexInputBindingDescription(
|
||||
VERTEX_BUFFER_BIND_ID,
|
||||
vkMeshLoader::vertexSize(vertexLayout),
|
||||
vk::VertexInputRate::eVertex);
|
||||
|
||||
// Attribute descriptions
|
||||
// Describes memory layout and shader positions
|
||||
vertices.attributeDescriptions.resize(3);
|
||||
// Location 0 : Position
|
||||
vertices.attributeDescriptions[0] =
|
||||
vk::VertexInputAttributeDescription(
|
||||
0,
|
||||
VERTEX_BUFFER_BIND_ID,
|
||||
vk::Format::eR32G32B32Sfloat,
|
||||
0);
|
||||
// Location 1 : Vertex normal
|
||||
vertices.attributeDescriptions[1] =
|
||||
vk::VertexInputAttributeDescription(
|
||||
1,
|
||||
VERTEX_BUFFER_BIND_ID,
|
||||
vk::Format::eR32G32B32Sfloat,
|
||||
sizeof(float) * 3);
|
||||
// Location 2 : Texture coordinates
|
||||
vertices.attributeDescriptions[2] =
|
||||
vk::VertexInputAttributeDescription(
|
||||
2,
|
||||
VERTEX_BUFFER_BIND_ID,
|
||||
vk::Format::eR32G32Sfloat,
|
||||
sizeof(float) * 6);
|
||||
|
||||
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
|
||||
TextureCubemap::setupDescriptorPool()
|
||||
{
|
||||
// Example uses one ubo and one image sampler
|
||||
std::vector<vk::DescriptorPoolSize> poolSizes =
|
||||
{
|
||||
{vk::DescriptorType::eUniformBuffer, 2},
|
||||
{vk::DescriptorType::eCombinedImageSampler, 2}
|
||||
};
|
||||
|
||||
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
|
||||
TextureCubemap::setupDescriptorSetLayout()
|
||||
{
|
||||
std::vector<vk::DescriptorSetLayoutBinding> setLayoutBindings =
|
||||
{
|
||||
// Binding 0 : Vertex shader uniform buffer
|
||||
{
|
||||
0,
|
||||
vk::DescriptorType::eUniformBuffer,
|
||||
1,
|
||||
vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment,
|
||||
},
|
||||
// Binding 1 : Fragment shader image sampler
|
||||
{
|
||||
1,
|
||||
vk::DescriptorType::eCombinedImageSampler,
|
||||
1,
|
||||
vk::ShaderStageFlagBits::eFragment
|
||||
},
|
||||
};
|
||||
|
||||
vk::DescriptorSetLayoutCreateInfo descriptorLayout(
|
||||
{},
|
||||
static_cast<uint32_t>(setLayoutBindings.size()),
|
||||
setLayoutBindings.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);
|
||||
|
||||
res = vkctx.device.createPipelineLayout(&pipelineLayoutCreateInfo,
|
||||
nullptr,
|
||||
&pipelineLayout);
|
||||
if (res != vk::Result::eSuccess) {
|
||||
throw bad_vulkan_alloc((int)res, "createPipelineLayout");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TextureCubemap::setupDescriptorSets()
|
||||
{
|
||||
vk::DescriptorSetAllocateInfo allocInfo(
|
||||
descriptorPool,
|
||||
1,
|
||||
&descriptorSetLayout);
|
||||
|
||||
vk::Result res
|
||||
= vkctx.device.allocateDescriptorSets(&allocInfo,
|
||||
&descriptorSets.object);
|
||||
if (res != vk::Result::eSuccess) {
|
||||
throw bad_vulkan_alloc((int)res, "allocateDescriptorSets");
|
||||
}
|
||||
|
||||
// Image descriptor for the cubemap texture
|
||||
vk::DescriptorImageInfo cubeMapDescriptor(
|
||||
sampler,
|
||||
imageView,
|
||||
vk::ImageLayout::eShaderReadOnlyOptimal);
|
||||
|
||||
std::vector<vk::WriteDescriptorSet> writeDescriptorSets =
|
||||
{
|
||||
// Binding 0 : Vertex shader uniform buffer
|
||||
vk::WriteDescriptorSet(
|
||||
descriptorSets.object,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
vk::DescriptorType::eUniformBuffer,
|
||||
nullptr,
|
||||
&uniformData.object.descriptor),
|
||||
// Binding 1 : Fragment shader cubemap sampler
|
||||
vk::WriteDescriptorSet(
|
||||
descriptorSets.object,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
vk::DescriptorType::eCombinedImageSampler,
|
||||
&cubeMapDescriptor)
|
||||
};
|
||||
|
||||
vkctx.device.updateDescriptorSets(
|
||||
static_cast<uint32_t>(writeDescriptorSets.size()),
|
||||
writeDescriptorSets.data(),
|
||||
0,
|
||||
nullptr);
|
||||
|
||||
// Sky box descriptor set
|
||||
res = vkctx.device.allocateDescriptorSets(&allocInfo,
|
||||
&descriptorSets.skybox);
|
||||
if (res != vk::Result::eSuccess) {
|
||||
throw bad_vulkan_alloc((int)res, "allocateDescriptorSets");
|
||||
}
|
||||
|
||||
writeDescriptorSets =
|
||||
{
|
||||
// Binding 0 : Vertex shader uniform buffer
|
||||
vk::WriteDescriptorSet(
|
||||
descriptorSets.skybox,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
vk::DescriptorType::eUniformBuffer,
|
||||
nullptr,
|
||||
&uniformData.skybox.descriptor),
|
||||
// Binding 1 : Fragment shader texture sampler
|
||||
vk::WriteDescriptorSet(
|
||||
descriptorSets.skybox,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
vk::DescriptorType::eCombinedImageSampler,
|
||||
&cubeMapDescriptor)
|
||||
};
|
||||
|
||||
vkctx.device.updateDescriptorSets(
|
||||
static_cast<uint32_t>(writeDescriptorSets.size()),
|
||||
writeDescriptorSets.data(),
|
||||
0,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
void
|
||||
TextureCubemap::preparePipelines()
|
||||
{
|
||||
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 = VK_FALSE;
|
||||
rasterizationState.polygonMode = vk::PolygonMode::eFill;
|
||||
rasterizationState.cullMode = vk::CullModeFlagBits::eBack;
|
||||
// Make the faces on the inside of the cube the front faces. The mesh
|
||||
// was designed with the exterior faces as the front faces for OpenGL's
|
||||
// default of GL_CCW.
|
||||
#if !USE_GL_RH_NDC
|
||||
rasterizationState.frontFace = vk::FrontFace::eCounterClockwise;
|
||||
#else
|
||||
rasterizationState.frontFace = vk::FrontFace::eClockwise;
|
||||
#endif
|
||||
rasterizationState.lineWidth = 1.0f;
|
||||
|
||||
vk::PipelineColorBlendAttachmentState blendAttachmentState;
|
||||
blendAttachmentState.blendEnable = VK_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 = VK_FALSE;
|
||||
depthStencilState.depthWriteEnable = VK_FALSE;
|
||||
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();
|
||||
shaderStages[0] = loadShader(filepath + "skybox.vert.spv",
|
||||
vk::ShaderStageFlagBits::eVertex);
|
||||
shaderStages[1] = loadShader(filepath + "skybox.frag.spv",
|
||||
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.skybox);
|
||||
if (res != vk::Result::eSuccess) {
|
||||
throw bad_vulkan_alloc((int)res, "createGraphicsPipelines");
|
||||
}
|
||||
|
||||
// Cube map reflect pipeline
|
||||
shaderStages[0] = loadShader(filepath + "reflect.vert.spv",
|
||||
vk::ShaderStageFlagBits::eVertex);
|
||||
shaderStages[1] = loadShader(filepath + "reflect.frag.spv",
|
||||
vk::ShaderStageFlagBits::eFragment);
|
||||
|
||||
// Enable depth test and write
|
||||
depthStencilState.depthWriteEnable = VK_TRUE;
|
||||
depthStencilState.depthTestEnable = VK_TRUE;
|
||||
// Flip cull mode
|
||||
rasterizationState.cullMode = vk::CullModeFlagBits::eFront;
|
||||
|
||||
res = vkctx.device.createGraphicsPipelines(vkctx.pipelineCache, 1,
|
||||
&pipelineCreateInfo, nullptr,
|
||||
&pipelines.reflect);
|
||||
if (res != vk::Result::eSuccess) {
|
||||
throw bad_vulkan_alloc((int)res, "createGraphicsPipelines");
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare and initialize uniform buffer containing shader uniforms
|
||||
void
|
||||
TextureCubemap::prepareUniformBuffers()
|
||||
{
|
||||
// 3D objact
|
||||
vkctx.createBuffer(
|
||||
vk::BufferUsageFlagBits::eUniformBuffer,
|
||||
vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent,
|
||||
sizeof(ubo),
|
||||
nullptr,
|
||||
&uniformData.object.buffer,
|
||||
&uniformData.object.memory,
|
||||
&uniformData.object.descriptor);
|
||||
|
||||
// Skybox
|
||||
vkctx.createBuffer(
|
||||
vk::BufferUsageFlagBits::eUniformBuffer,
|
||||
vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent,
|
||||
sizeof(ubo),
|
||||
nullptr,
|
||||
&uniformData.skybox.buffer,
|
||||
&uniformData.skybox.memory,
|
||||
&uniformData.skybox.descriptor);
|
||||
|
||||
updateUniformBuffers();
|
||||
}
|
||||
|
||||
void
|
||||
TextureCubemap::updateUniformBuffers()
|
||||
{
|
||||
// 3D object
|
||||
glm::mat4 viewMatrix = glm::mat4();
|
||||
ubo.projection = glm::perspective(glm::radians(60.0f),
|
||||
(float)w_width / (float)w_height,
|
||||
0.001f, 256.0f);
|
||||
viewMatrix = glm::translate(viewMatrix, glm::vec3(0.0f, 0.0f, zoom));
|
||||
|
||||
// Transform the z axis to what the viewer is perceiving as the z axis to make
|
||||
// the behaviour of 2-finger rotation (the value in rotation.z) understandable.
|
||||
glm::mat4 zAxisRotMatrix;
|
||||
zAxisRotMatrix = glm::rotate(zAxisRotMatrix, glm::radians(-rotation.x),
|
||||
glm::vec3(1.0f, 0.0f, 0.0f));
|
||||
zAxisRotMatrix = glm::rotate(zAxisRotMatrix, glm::radians(-rotation.y),
|
||||
glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
zRotationAxis = normalize(zAxisRotMatrix * glm::vec4(0.0f, 0.0f, 1.0f, 1.0f));
|
||||
|
||||
ubo.modelView = glm::mat4();
|
||||
ubo.modelView = viewMatrix * glm::translate(ubo.modelView, cameraPos);
|
||||
ubo.modelView = glm::rotate(ubo.modelView, glm::radians(rotation.x),
|
||||
glm::vec3(1.0f, 0.0f, 0.0f));
|
||||
ubo.modelView = glm::rotate(ubo.modelView, glm::radians(rotation.y),
|
||||
glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
ubo.modelView = glm::rotate(ubo.modelView, glm::radians(rotation.z),
|
||||
zRotationAxis);
|
||||
|
||||
// Do the inverse here because doing it in every fragment is a bit much.
|
||||
// Also MetalSL does not have inverse() and does not support passing
|
||||
// transforms between stages.
|
||||
ubo.invModelView = glm::inverse(ubo.modelView);
|
||||
|
||||
uint8_t *pData;
|
||||
VK_CHECK_RESULT(vkMapMemory(vkctx.device, uniformData.object.memory, 0, sizeof(ubo), 0, (void **)&pData));
|
||||
memcpy(pData, &ubo, sizeof(ubo));
|
||||
vkUnmapMemory(vkctx.device, uniformData.object.memory);
|
||||
|
||||
// Skybox
|
||||
// Remove translation from modelView so the skybox doesn't move.
|
||||
ubo.modelView = glm::mat4(glm::mat3(ubo.modelView));
|
||||
// Inverse not needed by skybox.
|
||||
|
||||
VK_CHECK_RESULT(vkMapMemory(vkctx.device, uniformData.skybox.memory, 0, sizeof(ubo), 0, (void **)&pData));
|
||||
memcpy(pData, &ubo, sizeof(ubo));
|
||||
vkUnmapMemory(vkctx.device, uniformData.skybox.memory);
|
||||
}
|
||||
|
||||
void
|
||||
TextureCubemap::prepareSamplerAndView()
|
||||
{
|
||||
// Create sampler.
|
||||
vk::SamplerCreateInfo samplerInfo;
|
||||
// Set the non-default values
|
||||
samplerInfo.magFilter = vk::Filter::eLinear;
|
||||
samplerInfo.minFilter = vk::Filter::eLinear;
|
||||
samplerInfo.mipmapMode = vk::SamplerMipmapMode::eLinear;
|
||||
samplerInfo.maxLod = (float)cubeMap.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;
|
||||
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 = cubeMap.image;
|
||||
viewInfo.format = static_cast<vk::Format>(cubeMap.imageFormat);
|
||||
viewInfo.viewType = static_cast<vk::ImageViewType>(cubeMap.viewType);
|
||||
viewInfo.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor;
|
||||
viewInfo.subresourceRange.layerCount = cubeMap.layerCount;
|
||||
viewInfo.subresourceRange.levelCount = cubeMap.levelCount;
|
||||
imageView = vkctx.device.createImageView(viewInfo);
|
||||
}
|
||||
|
||||
void
|
||||
TextureCubemap::prepare()
|
||||
{
|
||||
prepareSamplerAndView();
|
||||
loadMeshes();
|
||||
setupVertexDescriptions();
|
||||
prepareUniformBuffers();
|
||||
setupDescriptorSetLayout();
|
||||
preparePipelines();
|
||||
setupDescriptorPool();
|
||||
setupDescriptorSets();
|
||||
vkctx.createDrawCommandBuffers();
|
||||
buildCommandBuffers();
|
||||
}
|
||||
|
||||
void
|
||||
TextureCubemap::toggleSkyBox()
|
||||
{
|
||||
displaySkybox = !displaySkybox;
|
||||
rebuildCommandBuffers();
|
||||
}
|
||||
|
||||
void
|
||||
TextureCubemap::toggleObject(int direction)
|
||||
{
|
||||
meshes.objectIndex += direction;
|
||||
if (meshes.objectIndex >= static_cast<int32_t>(meshes.objects.size())) {
|
||||
meshes.objectIndex = 0;
|
||||
} else if (meshes.objectIndex < 0) {
|
||||
meshes.objectIndex = static_cast<int32_t>(meshes.objects.size()) - 1;
|
||||
}
|
||||
rebuildCommandBuffers();
|
||||
}
|
||||
|
||||
void
|
||||
TextureCubemap::changeLodBias(float delta)
|
||||
{
|
||||
ubo.lodBias += delta;
|
||||
if (ubo.lodBias < 0.0f)
|
||||
{
|
||||
ubo.lodBias = 0.0f;
|
||||
}
|
||||
if (ubo.lodBias > cubeMap.levelCount)
|
||||
{
|
||||
ubo.lodBias = (float)cubeMap.levelCount;
|
||||
}
|
||||
updateUniformBuffers();
|
||||
//updateTextOverlay();
|
||||
}
|
||||
|
||||
void
|
||||
TextureCubemap::keyPressed(uint32_t keyCode)
|
||||
{
|
||||
switch (keyCode)
|
||||
{
|
||||
case 's':
|
||||
toggleSkyBox();
|
||||
break;
|
||||
case ' ':
|
||||
toggleObject(+1);
|
||||
break;
|
||||
case SDLK_KP_PLUS:
|
||||
changeLodBias(0.1f);
|
||||
break;
|
||||
case SDLK_KP_MINUS:
|
||||
changeLodBias(-0.1f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TextureCubemap::getOverlayText(VulkanTextOverlay *textOverlay, float yOffset)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << std::setprecision(2) << std::fixed << ubo.lodBias;
|
||||
textOverlay->addText("Press \"s\" to toggle skybox", 5.0f,
|
||||
yOffset, VulkanTextOverlay::alignLeft);
|
||||
textOverlay->addText("Press \"space\" or 2-finger swipe up or down to change object", 5.0f,
|
||||
yOffset+20.0f, VulkanTextOverlay::alignLeft);
|
||||
textOverlay->addText("LOD bias: " + ss.str() + " (numpad +/- to change)",
|
||||
5.0f, yOffset+40.0f, VulkanTextOverlay::alignLeft);
|
||||
}
|
||||
|
||||
const char*
|
||||
TextureCubemap::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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
/* -*- tab-width: 4; -*- */
|
||||
/* vi: set sw=2 ts=4 expandtab: */
|
||||
|
||||
/*
|
||||
* Copyright 2017-2020 Mark Callow.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "VulkanLoadTestSample.h"
|
||||
|
||||
#include <ktxvulkan.h>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
#define VERTEX_BUFFER_BIND_ID 0
|
||||
#define ENABLE_VALIDATION false
|
||||
|
||||
class TextureCubemap : public VulkanLoadTestSample
|
||||
{
|
||||
public:
|
||||
TextureCubemap(VulkanContext& vkctx,
|
||||
uint32_t width, uint32_t height,
|
||||
const char* const szArgs,
|
||||
const std::string sBasePath, int32_t yflip);
|
||||
~TextureCubemap();
|
||||
|
||||
virtual int doEvent(SDL_Event* event);
|
||||
virtual void resize(uint32_t width, uint32_t height);
|
||||
virtual void run(uint32_t msTicks);
|
||||
|
||||
virtual void getOverlayText(VulkanTextOverlay *textOverlay, float yOffset);
|
||||
virtual const char* customizeTitle(const char* const title);
|
||||
|
||||
static VulkanLoadTestSample*
|
||||
create(VulkanContext& vkctx,
|
||||
uint32_t width, uint32_t height,
|
||||
const char* const szArgs, const std::string sBasePath);
|
||||
|
||||
protected:
|
||||
int preloadImages = 0;
|
||||
|
||||
bool displaySkybox = true;
|
||||
|
||||
bool transcoded;
|
||||
vk::Format transcodedFormat;
|
||||
std::string title;
|
||||
|
||||
ktxVulkanTexture cubeMap;
|
||||
vk::Sampler sampler;
|
||||
vk::ImageView imageView;
|
||||
|
||||
glm::vec3 zRotationAxis;
|
||||
|
||||
struct {
|
||||
vk::PipelineVertexInputStateCreateInfo inputState;
|
||||
std::vector<vk::VertexInputBindingDescription> bindingDescriptions;
|
||||
std::vector<vk::VertexInputAttributeDescription> attributeDescriptions;
|
||||
} vertices;
|
||||
|
||||
struct {
|
||||
vkMeshLoader::MeshBuffer skybox;
|
||||
std::vector<vkMeshLoader::MeshBuffer> objects;
|
||||
int32_t objectIndex = 0;
|
||||
} meshes;
|
||||
|
||||
struct {
|
||||
UniformData object;
|
||||
UniformData skybox;
|
||||
} uniformData;
|
||||
|
||||
struct {
|
||||
glm::mat4 projection;
|
||||
glm::mat4 modelView;
|
||||
glm::mat4 invModelView;
|
||||
glm::mat4 uvwTransform;
|
||||
float lodBias = 0.0f;
|
||||
} ubo;
|
||||
|
||||
struct {
|
||||
vk::Pipeline skybox;
|
||||
vk::Pipeline reflect;
|
||||
} pipelines;
|
||||
|
||||
struct {
|
||||
vk::DescriptorSet object;
|
||||
vk::DescriptorSet skybox;
|
||||
} descriptorSets;
|
||||
|
||||
vk::PipelineLayout pipelineLayout;
|
||||
vk::DescriptorSetLayout descriptorSetLayout;
|
||||
vk::DescriptorPool descriptorPool;
|
||||
|
||||
void cleanup();
|
||||
|
||||
void rebuildCommandBuffers();
|
||||
void buildCommandBuffers();
|
||||
void loadMeshes();
|
||||
|
||||
void setupVertexDescriptions();
|
||||
void setupDescriptorPool();
|
||||
void setupDescriptorSetLayout();
|
||||
void setupDescriptorSets();
|
||||
void preparePipelines();
|
||||
|
||||
// Prepare and initialize uniform buffer containing shader uniforms
|
||||
void prepareUniformBuffers();
|
||||
void updateUniformBuffers();
|
||||
void prepareSamplerAndView();
|
||||
void prepare();
|
||||
|
||||
void toggleSkyBox();
|
||||
void toggleObject(int direction);
|
||||
void changeLodBias(float delta);
|
||||
|
||||
void processArgs(std::string sArgs);
|
||||
|
||||
virtual void keyPressed(uint32_t keyCode);
|
||||
virtual void viewChanged()
|
||||
{
|
||||
updateUniformBuffers();
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,77 @@
|
||||
/* -*- tab-width: 4; -*- */
|
||||
/* vi: set sw=2 ts=4 expandtab: */
|
||||
|
||||
/*
|
||||
* Copyright 2017-2020 Mark Callow.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @class TextureMipmap
|
||||
* @~English
|
||||
*
|
||||
* @brief Definition of test sample for loading and displaying all the levels of a 2D mipmapped texture.
|
||||
*
|
||||
* @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 <assert.h>
|
||||
#include <algorithm>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <vector>
|
||||
|
||||
#include "TextureMipmap.h"
|
||||
#include "ltexceptions.h"
|
||||
|
||||
#include <ktxvulkan.h>
|
||||
|
||||
VulkanLoadTestSample*
|
||||
TextureMipmap::create(VulkanContext& vkctx,
|
||||
uint32_t width, uint32_t height,
|
||||
const char* const szArgs, const std::string sBasePath)
|
||||
{
|
||||
return new TextureMipmap(vkctx, width, height, szArgs, sBasePath);
|
||||
}
|
||||
|
||||
#define INSTANCE_COUNT_CONST_ID 1
|
||||
#define INSTANCES_DECLARED_IN_SHADER 16
|
||||
|
||||
TextureMipmap::TextureMipmap(VulkanContext& vkctx,
|
||||
uint32_t width, uint32_t height,
|
||||
const char* const szArgs, const std::string sBasePath)
|
||||
: InstancedSampleBase(vkctx, width, height, szArgs, sBasePath)
|
||||
{
|
||||
zoom = -18.0f;
|
||||
|
||||
if (texture.levelCount == 1) {
|
||||
std::stringstream message;
|
||||
|
||||
message << "TextureMipmap requires a mipmapped texture.";
|
||||
throw std::runtime_error(message.str());
|
||||
}
|
||||
|
||||
try {
|
||||
prepare("instancinglod.frag.spv", "instancinglod.vert.spv",
|
||||
INSTANCE_COUNT_CONST_ID, texture.levelCount,
|
||||
INSTANCES_DECLARED_IN_SHADER);
|
||||
|
||||
} catch (std::exception& e) {
|
||||
(void)e; // To quiet unused variable warnings from some compilers.
|
||||
// For reasons I don't understand ~InstancedSampleBase is called
|
||||
// during the throw before the exception is caught. ~InstancedSampleBase
|
||||
// also calls cleanup(). In Texture, an identically structured class
|
||||
// which does not have a base class between it and LoadTestSample, the
|
||||
// destructor is not called during throw.
|
||||
//cleanup();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/* -*- tab-width: 4; -*- */
|
||||
/* vi: set sw=2 ts=4 expandtab: */
|
||||
|
||||
/*
|
||||
* Copyright 2017-2020 Mark Callow.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef _TEXTURE_MIPMAP_H_
|
||||
#define _TEXTURE_MIPMAP_H_
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @file
|
||||
* @~English
|
||||
*
|
||||
* @brief Declaration of test sample for loading and displaying all the levels of a 2D mipmapped texture.
|
||||
*
|
||||
* @author Mark Callow, github.com/MarkCallow.
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "InstancedSampleBase.h"
|
||||
|
||||
#include <ktxvulkan.h>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
class TextureMipmap : public InstancedSampleBase
|
||||
{
|
||||
public:
|
||||
TextureMipmap(VulkanContext& vkctx,
|
||||
uint32_t width, uint32_t height,
|
||||
const char* const szArgs,
|
||||
const std::string sBasePath);
|
||||
|
||||
static VulkanLoadTestSample*
|
||||
create(VulkanContext& vkctx,
|
||||
uint32_t width, uint32_t height,
|
||||
const char* const szArgs, const std::string sBasePath);
|
||||
};
|
||||
|
||||
#endif /* _TEXTURE_MIPMAP_H_ */
|
||||
@@ -0,0 +1,729 @@
|
||||
/* -*- 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);
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/* -*- tab-width: 4; -*- */
|
||||
/* vi: set sw=2 ts=4 expandtab: */
|
||||
|
||||
/*
|
||||
* Copyright 2017-2020 Mark Callow.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef TEXTURED_CUBE_H
|
||||
#define TEXTURED_CUBE_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "VulkanLoadTestSample.h"
|
||||
|
||||
#include <ktxvulkan.h>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
using namespace glm;
|
||||
|
||||
class TexturedCube : public VulkanLoadTestSample {
|
||||
public:
|
||||
TexturedCube(VulkanContext& vkctx,
|
||||
uint32_t width, uint32_t height,
|
||||
const char* const szArgs,
|
||||
const std::string sBasePath);
|
||||
virtual ~TexturedCube();
|
||||
|
||||
virtual void resize(uint32_t width, uint32_t height);
|
||||
virtual void run(uint32_t msTicks);
|
||||
|
||||
//virtual void getOverlayText(VulkanTextOverlay *textOverlay);
|
||||
|
||||
static VulkanLoadTestSample*
|
||||
create(VulkanContext& vkctx,
|
||||
uint32_t width, uint32_t height,
|
||||
const char* const szArgs, const std::string sBasePath);
|
||||
|
||||
protected:
|
||||
void buildCommandBuffer(const int bufferIndex);
|
||||
void cleanup();
|
||||
void createDescriptorSetLayout();
|
||||
void prepareCubeDataBuffers();
|
||||
void prepareDescriptorPool();
|
||||
void prepareDescriptorSet();
|
||||
void preparePipeline();
|
||||
void prepareUniformBuffer();
|
||||
#if defined(INCLUDE_SHADERS)
|
||||
VkShaderModule createShaderModule(uint32_t* spv, size_t size);
|
||||
VkShaderModule prepareFragShader();
|
||||
VkShaderModule prepareVertShader();
|
||||
#endif
|
||||
void setupVertexDescriptions();
|
||||
void updateUniformBuffer();
|
||||
|
||||
struct {
|
||||
vk::Buffer buf;
|
||||
vk::DeviceMemory mem;
|
||||
vk::PipelineVertexInputStateCreateInfo inputState;
|
||||
std::vector<vk::VertexInputBindingDescription> bindingDescriptions;
|
||||
std::vector<vk::VertexInputAttributeDescription> attributeDescriptions;
|
||||
} vertices;
|
||||
|
||||
struct {
|
||||
int count;
|
||||
vk::Buffer buf;
|
||||
vk::DeviceMemory mem;
|
||||
} indices;
|
||||
|
||||
UniformData uniformData;
|
||||
|
||||
struct Uniforms {
|
||||
mat4 projection;
|
||||
mat4 model;
|
||||
mat4 view;
|
||||
vec4 viewPos;
|
||||
float lodBias;
|
||||
} uniforms;
|
||||
|
||||
float zoom;
|
||||
vec3 rotation;
|
||||
vec3 cameraPos;
|
||||
|
||||
#if 0
|
||||
mat4 model;
|
||||
mat4 view;
|
||||
mat4 projection;
|
||||
vec3 viewPos;
|
||||
#endif
|
||||
|
||||
const uint32_t numTextures;
|
||||
|
||||
VkDescriptorPool descriptorPool;
|
||||
VkDescriptorSet descriptorSet;
|
||||
VkDescriptorSetLayout descriptorSetLayout;
|
||||
VkPipelineLayout pipelineLayout;
|
||||
VkPipelineCache pipelineCache;
|
||||
VkPipeline pipeline;
|
||||
VkShaderModule vsModule;
|
||||
VkShaderModule fsModule;
|
||||
};
|
||||
|
||||
#endif /* TEXTURED_CUBE_H */
|
||||
@@ -0,0 +1,189 @@
|
||||
/* -*- tab-width: 4; -*- */
|
||||
/* vi: set sw=2 ts=4 expandtab: */
|
||||
|
||||
/*
|
||||
* Copyright 2017-2020 Mark Callow.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @class VulkanLoadTestSample
|
||||
* @~English
|
||||
*
|
||||
* @brief Base class for Vulkan texture loading test samples.
|
||||
*
|
||||
* @author Mark Callow, github.com/MarkCallow.
|
||||
*/
|
||||
|
||||
#include "VulkanLoadTestSample.h"
|
||||
|
||||
#include <random>
|
||||
#include <unordered_map>
|
||||
|
||||
#define VMA_IMPLEMENTATION
|
||||
#define VMA_VULKAN_VERSION 1000000
|
||||
#define VMA_STATIC_VULKAN_FUNCTIONS 0
|
||||
#define VMA_DYNAMIC_VULKAN_FUNCTIONS 1
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4100)
|
||||
#pragma warning(disable: 4189)
|
||||
#pragma warning(disable: 4324)
|
||||
#endif
|
||||
|
||||
#include "vma/vk_mem_alloc.h"
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
namespace VMA_CALLBACKS
|
||||
{
|
||||
VmaAllocator vmaAllocator;
|
||||
std::mt19937_64 mt64;
|
||||
VkPhysicalDeviceMemoryProperties cachedDevMemProps;
|
||||
|
||||
void InitVMA(VkPhysicalDevice& physicalDevice, VkDevice& device, VkInstance& instance,
|
||||
VkPhysicalDeviceMemoryProperties& devMemProps)
|
||||
{
|
||||
cachedDevMemProps = devMemProps;
|
||||
|
||||
VmaVulkanFunctions vulkanFunctions = {};
|
||||
vulkanFunctions.vkGetInstanceProcAddr = &vkGetInstanceProcAddr;
|
||||
vulkanFunctions.vkGetDeviceProcAddr = &vkGetDeviceProcAddr;
|
||||
|
||||
VmaAllocatorCreateInfo allocatorCreateInfo = {};
|
||||
allocatorCreateInfo.vulkanApiVersion = VK_API_VERSION_1_0;
|
||||
allocatorCreateInfo.physicalDevice = physicalDevice;
|
||||
allocatorCreateInfo.device = device;
|
||||
allocatorCreateInfo.instance = instance;
|
||||
allocatorCreateInfo.pVulkanFunctions = &vulkanFunctions;
|
||||
|
||||
vmaCreateAllocator(&allocatorCreateInfo, &vmaAllocator);
|
||||
}
|
||||
|
||||
void DestroyVMA()
|
||||
{
|
||||
vmaDestroyAllocator(vmaAllocator);
|
||||
}
|
||||
|
||||
struct AllocationInfo
|
||||
{
|
||||
VmaAllocation allocation;
|
||||
VkDeviceSize mapSize;
|
||||
};
|
||||
std::unordered_map<uint64_t, AllocationInfo> AllocMemCWrapperDirectory;
|
||||
uint64_t AllocMemCWrapper(VkMemoryAllocateInfo* allocInfo, VkMemoryRequirements* memReq, uint64_t* numPages)
|
||||
{
|
||||
uint64_t allocId = mt64();
|
||||
VmaAllocationCreateInfo pCreateInfo = {};
|
||||
if ((cachedDevMemProps.memoryTypes[allocInfo->memoryTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) ||
|
||||
(cachedDevMemProps.memoryTypes[allocInfo->memoryTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT))
|
||||
{
|
||||
pCreateInfo.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
|
||||
pCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT;
|
||||
}
|
||||
else
|
||||
{
|
||||
pCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
|
||||
}
|
||||
pCreateInfo.memoryTypeBits = memReq->memoryTypeBits;
|
||||
|
||||
VmaAllocation allocation;
|
||||
VkResult result = vmaAllocateMemory(vmaAllocator, memReq, &pCreateInfo, &allocation, VMA_NULL);
|
||||
if (result != VK_SUCCESS)
|
||||
{
|
||||
return 0ull;
|
||||
}
|
||||
|
||||
AllocMemCWrapperDirectory[allocId].allocation = allocation;
|
||||
AllocMemCWrapperDirectory[allocId].mapSize = memReq->size;
|
||||
*numPages = 1ull;
|
||||
|
||||
return allocId;
|
||||
}
|
||||
|
||||
VkResult BindBufferMemoryCWrapper(VkBuffer buffer, uint64_t allocId)
|
||||
{
|
||||
return vmaBindBufferMemory(vmaAllocator, AllocMemCWrapperDirectory[allocId].allocation, buffer);
|
||||
}
|
||||
|
||||
VkResult BindImageMemoryCWrapper(VkImage image, uint64_t allocId)
|
||||
{
|
||||
return vmaBindImageMemory(vmaAllocator, AllocMemCWrapperDirectory[allocId].allocation, image);
|
||||
}
|
||||
|
||||
VkResult MapMemoryCWrapper(uint64_t allocId, uint64_t, VkDeviceSize* mapLength, void** dataPtr)
|
||||
{
|
||||
*mapLength = AllocMemCWrapperDirectory[allocId].mapSize;
|
||||
return vmaMapMemory(vmaAllocator, AllocMemCWrapperDirectory[allocId].allocation, dataPtr);
|
||||
}
|
||||
|
||||
void UnmapMemoryCWrapper(uint64_t allocId, uint64_t)
|
||||
{
|
||||
vmaUnmapMemory(vmaAllocator, AllocMemCWrapperDirectory[allocId].allocation);
|
||||
}
|
||||
|
||||
void FreeMemCWrapper(uint64_t allocId)
|
||||
{
|
||||
vmaFreeMemory(vmaAllocator, AllocMemCWrapperDirectory[allocId].allocation);
|
||||
AllocMemCWrapperDirectory.erase(allocId);
|
||||
}
|
||||
}
|
||||
|
||||
VulkanLoadTestSample::~VulkanLoadTestSample()
|
||||
{
|
||||
for (auto& shaderModule : shaderModules)
|
||||
{
|
||||
vkctx.device.destroyShaderModule(shaderModule, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
VulkanLoadTestSample::loadMesh(std::string filename,
|
||||
vkMeshLoader::MeshBuffer* meshBuffer,
|
||||
std::vector<vkMeshLoader::VertexLayout> vertexLayout,
|
||||
float scale)
|
||||
{
|
||||
VulkanMeshLoader *mesh = new VulkanMeshLoader();
|
||||
|
||||
if (!mesh->LoadMesh(filename)) {
|
||||
std::stringstream message;
|
||||
message << "Error reading or parsing mesh file \"" << filename << "\"";
|
||||
throw std::runtime_error(message.str());
|
||||
}
|
||||
|
||||
assert(mesh->m_Entries.size() > 0);
|
||||
|
||||
vk::CommandBuffer copyCmd =
|
||||
vkctx.createCommandBuffer(vk::CommandBufferLevel::ePrimary, false);
|
||||
|
||||
mesh->createBuffers(
|
||||
vkctx.device,
|
||||
static_cast<VkPhysicalDeviceMemoryProperties>(vkctx.memoryProperties),
|
||||
meshBuffer,
|
||||
vertexLayout,
|
||||
scale,
|
||||
true,
|
||||
copyCmd,
|
||||
vkctx.queue);
|
||||
|
||||
vkctx.device.freeCommandBuffers(vkctx.commandPool, 1, ©Cmd);
|
||||
|
||||
meshBuffer->dim = mesh->dim.size;
|
||||
|
||||
delete(mesh);
|
||||
}
|
||||
|
||||
vk::PipelineShaderStageCreateInfo
|
||||
VulkanLoadTestSample::loadShader(std::string filename,
|
||||
vk::ShaderStageFlagBits stage,
|
||||
const char* modname)
|
||||
{
|
||||
vk::PipelineShaderStageCreateInfo shaderStage = vkctx.loadShader(filename, stage, modname);
|
||||
assert(shaderStage.module);
|
||||
shaderModules.push_back(shaderStage.module);
|
||||
return shaderStage;
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
/* -*- tab-width: 4; -*- */
|
||||
/* vi: set sw=2 ts=4 expandtab: */
|
||||
|
||||
#ifndef VULKAN_LOAD_TEST_SAMPLE_H
|
||||
#define VULKAN_LOAD_TEST_SAMPLE_H
|
||||
|
||||
/*
|
||||
* Copyright 2017-2020 Mark Callow.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
|
||||
#include "LoadTestSample.h"
|
||||
#include "VulkanAppSDL.h"
|
||||
#include "VulkanContext.h"
|
||||
// MeshLoader needs vulkantools.h to be already included. It is included by
|
||||
// vulkantextoverlay.hpp via VulkanAppSDL.h.
|
||||
#include "utils/VulkanMeshLoader.hpp"
|
||||
|
||||
#include <ktxvulkan.h>
|
||||
|
||||
namespace VMA_CALLBACKS
|
||||
{
|
||||
void InitVMA(VkPhysicalDevice& physicalDevice, VkDevice& device, VkInstance& instance,
|
||||
VkPhysicalDeviceMemoryProperties& devMemProps);
|
||||
void DestroyVMA();
|
||||
uint64_t AllocMemCWrapper(VkMemoryAllocateInfo* allocInfo, VkMemoryRequirements* memReq, uint64_t* numPages);
|
||||
VkResult BindBufferMemoryCWrapper(VkBuffer buffer, uint64_t allocId);
|
||||
VkResult BindImageMemoryCWrapper(VkImage image, uint64_t allocId);
|
||||
VkResult MapMemoryCWrapper(uint64_t allocId, uint64_t, VkDeviceSize* mapLength, void** dataPtr);
|
||||
void UnmapMemoryCWrapper(uint64_t allocId, uint64_t);
|
||||
void FreeMemCWrapper(uint64_t allocId);
|
||||
}
|
||||
|
||||
#define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))
|
||||
|
||||
extern "C" const char* vkFormatString(VkFormat format);
|
||||
|
||||
class VulkanLoadTestSample : public LoadTestSample {
|
||||
public:
|
||||
typedef uint64_t ticks_t;
|
||||
VulkanLoadTestSample(VulkanContext& vkctx,
|
||||
uint32_t width, uint32_t height,
|
||||
/* const char* const szArgs, */
|
||||
const std::string sBasePath, int32_t yflip = -1)
|
||||
: LoadTestSample(width, height, /*szArgs,*/ sBasePath, yflip),
|
||||
vkctx(vkctx),
|
||||
defaultClearColor(std::array<float,4>({0.025f, 0.025f, 0.025f, 1.0f}))
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~VulkanLoadTestSample();
|
||||
virtual int doEvent(SDL_Event* event) { return LoadTestSample::doEvent(event); };
|
||||
virtual void resize(uint32_t width, uint32_t height) = 0;
|
||||
virtual void run(uint32_t msTicks) = 0;
|
||||
|
||||
virtual void getOverlayText(VulkanTextOverlay* /*textOverlay*/,
|
||||
float /*yoffset*/) { }
|
||||
virtual const char* customizeTitle(const char* const title) {
|
||||
return title;
|
||||
}
|
||||
|
||||
typedef VulkanLoadTestSample* (*PFN_create)(VulkanContext&,
|
||||
uint32_t width, uint32_t height,
|
||||
const char* const szArgs,
|
||||
const std::string sBasePath);
|
||||
|
||||
protected:
|
||||
|
||||
ktxVulkanTexture_subAllocatorCallbacks subAllocatorCallbacks = {
|
||||
VMA_CALLBACKS::AllocMemCWrapper,
|
||||
VMA_CALLBACKS::BindBufferMemoryCWrapper,
|
||||
VMA_CALLBACKS::BindImageMemoryCWrapper,
|
||||
VMA_CALLBACKS::MapMemoryCWrapper,
|
||||
VMA_CALLBACKS::UnmapMemoryCWrapper,
|
||||
VMA_CALLBACKS::FreeMemCWrapper
|
||||
};
|
||||
|
||||
virtual void keyPressed(uint32_t /*keyCode*/) { }
|
||||
virtual void viewChanged() { }
|
||||
bool gpuSupportsSwizzle() {
|
||||
return vkctx.gpuSupportsSwizzle();
|
||||
}
|
||||
|
||||
std::string ktxfilename;
|
||||
int externalFile = 0;
|
||||
|
||||
vk::PipelineShaderStageCreateInfo
|
||||
loadShader(std::string filename,
|
||||
vk::ShaderStageFlagBits stage,
|
||||
const char* modname = "main");
|
||||
void loadMesh(std::string filename,
|
||||
vkMeshLoader::MeshBuffer* meshBuffer,
|
||||
std::vector<vkMeshLoader::VertexLayout> vertexLayout,
|
||||
float scale);
|
||||
|
||||
struct MeshBufferInfo
|
||||
{
|
||||
vk::Buffer buf;
|
||||
vk::DeviceMemory mem;
|
||||
vk::DeviceSize size = 0;
|
||||
|
||||
void freeResources(vk::Device& device) {
|
||||
if (buf) device.destroyBuffer(buf);
|
||||
if (mem) device.freeMemory(mem);
|
||||
}
|
||||
};
|
||||
|
||||
struct MeshBuffer
|
||||
{
|
||||
MeshBufferInfo vertices;
|
||||
MeshBufferInfo indices;
|
||||
uint32_t indexCount;
|
||||
glm::vec3 dim;
|
||||
|
||||
void freeResources(vk::Device& device) {
|
||||
vertices.freeResources(device);
|
||||
indices.freeResources(device);
|
||||
}
|
||||
};
|
||||
|
||||
struct UniformData
|
||||
{
|
||||
vk::Buffer buffer;
|
||||
vk::DeviceMemory memory;
|
||||
vk::DescriptorBufferInfo descriptor;
|
||||
uint32_t allocSize;
|
||||
void* mapped = nullptr;
|
||||
|
||||
void freeResources(vk::Device& device) {
|
||||
device.destroyBuffer(buffer);
|
||||
device.freeMemory(memory);
|
||||
}
|
||||
};
|
||||
|
||||
VulkanContext& vkctx;
|
||||
|
||||
// Saved for clean-up
|
||||
std::vector<VkShaderModule> shaderModules;
|
||||
|
||||
const vk::ClearColorValue defaultClearColor;
|
||||
};
|
||||
|
||||
#endif /* VULKAN_LOAD_TEST_SAMPLE_H */
|
||||
@@ -0,0 +1,547 @@
|
||||
/* -*- tab-width: 4; -*- */
|
||||
/* vi: set sw=2 ts=4 expandtab: */
|
||||
|
||||
/*
|
||||
* Copyright 2017-2020 Mark Callow.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @class VulkanLoadTests
|
||||
* @~English
|
||||
*
|
||||
* @brief Framework for Vulkan texture loading test samples.
|
||||
*
|
||||
* @author Mark Callow, github.com/MarkCallow.
|
||||
*/
|
||||
|
||||
#include <exception>
|
||||
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <math.h>
|
||||
#include <string>
|
||||
#include <regex>
|
||||
|
||||
#include "VulkanLoadTests.h"
|
||||
#include "Texture.h"
|
||||
#include "Texture3d.h"
|
||||
#include "TextureArray.h"
|
||||
#include "TextureCubemap.h"
|
||||
#include "TexturedCube.h"
|
||||
#include "TextureMipmap.h"
|
||||
#include "ltexceptions.h"
|
||||
|
||||
#define LT_VK_MAJOR_VERSION 1
|
||||
#define LT_VK_MINOR_VERSION 0
|
||||
#define LT_VK_PATCH_VERSION 0
|
||||
#define LT_VK_VERSION VK_MAKE_VERSION(1, 0, 0)
|
||||
|
||||
VulkanLoadTests::VulkanLoadTests(const sampleInvocation samples[],
|
||||
const uint32_t numSamples,
|
||||
const char* const name)
|
||||
: VulkanAppSDL(name, 1280, 720, LT_VK_VERSION, true),
|
||||
siSamples(samples), sampleIndex(numSamples)
|
||||
{
|
||||
pCurSample = nullptr;
|
||||
}
|
||||
|
||||
VulkanLoadTests::~VulkanLoadTests()
|
||||
{
|
||||
delete pCurSample;
|
||||
}
|
||||
|
||||
bool
|
||||
VulkanLoadTests::initialize(Args& args)
|
||||
{
|
||||
if (!VulkanAppSDL::initialize(args))
|
||||
return false;
|
||||
|
||||
for (auto it = args.begin() + 1; it != args.end(); it++) {
|
||||
infiles.push_back(*it);
|
||||
}
|
||||
if (infiles.size() > 0) {
|
||||
sampleIndex.setNumSamples((uint32_t)infiles.size());
|
||||
}
|
||||
// Launch the first sample.
|
||||
invokeSample(Direction::eForward);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
VulkanLoadTests::finalize()
|
||||
{
|
||||
delete pCurSample;
|
||||
VulkanAppSDL::finalize();
|
||||
}
|
||||
|
||||
bool
|
||||
VulkanLoadTests::doEvent(SDL_Event* event)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
switch (event->type) {
|
||||
case SDL_EVENT_KEY_UP:
|
||||
switch (event->key.key) {
|
||||
case 'q':
|
||||
quit = true;
|
||||
break;
|
||||
case 'n':
|
||||
++sampleIndex;
|
||||
invokeSample(Direction::eForward);
|
||||
break;
|
||||
case 'p':
|
||||
--sampleIndex;
|
||||
invokeSample(Direction::eBack);
|
||||
break;
|
||||
default:
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
|
||||
// On macOS drop events come also when Launch Pad sends a file open event.
|
||||
case SDL_EVENT_DROP_BEGIN:
|
||||
// Opens of multiple selected files from Finder/LaunchPad come as
|
||||
// a BEGIN, COMPLETE sequence per file. Only clear infiles after a
|
||||
// suitable pause between COMPLETE and BEGIN.
|
||||
if (event->drop.timestamp - dropCompleteTime > 500) {
|
||||
infiles.clear();
|
||||
}
|
||||
break;
|
||||
case SDL_EVENT_DROP_FILE:
|
||||
infiles.push_back(event->drop.data);
|
||||
break;
|
||||
case SDL_EVENT_DROP_COMPLETE:
|
||||
if (!infiles.empty()) {
|
||||
// Guard against the drop being text.
|
||||
dropCompleteTime = event->drop.timestamp;
|
||||
sampleIndex.setNumSamples((uint32_t)infiles.size());
|
||||
invokeSample(Direction::eForward);
|
||||
}
|
||||
break;
|
||||
case SDL_EVENT_USER:
|
||||
if (event->user.code == SwipeDetector::swipeGesture) {
|
||||
SwipeDetector::Direction direction
|
||||
= SwipeDetector::pointerToDirection(event->user.data1);
|
||||
switch (direction) {
|
||||
case SwipeDetector::Direction::left:
|
||||
++sampleIndex;
|
||||
//SDL_Log("*********** Sample changed by swipe left **********");
|
||||
invokeSample(Direction::eForward);
|
||||
break;
|
||||
case SwipeDetector::Direction::right:
|
||||
--sampleIndex;
|
||||
//SDL_Log("*********** Sample changed by swipe right *********");
|
||||
invokeSample(Direction::eBack);
|
||||
break;
|
||||
default:
|
||||
result = true;
|
||||
}
|
||||
} else {
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
result = swipeDetector.doEvent(event);
|
||||
}
|
||||
|
||||
if (result) {
|
||||
// Further processing required.
|
||||
if (pCurSample != nullptr)
|
||||
result = pCurSample->doEvent(event); // Give sample a chance.
|
||||
if (result)
|
||||
return VulkanAppSDL::doEvent(event); // Pass to base class.
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VulkanLoadTests::windowResized()
|
||||
{
|
||||
if (pCurSample != nullptr)
|
||||
pCurSample->resize(w_width, w_height);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VulkanLoadTests::drawFrame(uint32_t msTicks)
|
||||
{
|
||||
if (pCurSample != nullptr)
|
||||
pCurSample->run(msTicks);
|
||||
|
||||
VulkanAppSDL::drawFrame(msTicks);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VulkanLoadTests::getOverlayText(float yOffset)
|
||||
{
|
||||
if (enableTextOverlay) {
|
||||
textOverlay->addText("Press \"n\" or 2-finger swipe left for next "
|
||||
"sample, \"p\" or swipe right for previous.",
|
||||
5.0f, yOffset, VulkanTextOverlay::alignLeft);
|
||||
yOffset += 20;
|
||||
textOverlay->addText("2-finger rotate or left mouse + drag to rotate.",
|
||||
5.0f, yOffset, VulkanTextOverlay::alignLeft);
|
||||
yOffset += 20;
|
||||
textOverlay->addText("Pinch/zoom or right mouse + drag to change "
|
||||
"object size.",
|
||||
5.0f, yOffset, VulkanTextOverlay::alignLeft);
|
||||
yOffset += 20;
|
||||
if (pCurSample != nullptr) {
|
||||
pCurSample->getOverlayText(textOverlay, yOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
VulkanLoadTests::invokeSample(Direction dir)
|
||||
{
|
||||
const sampleInvocation* sampleInv
|
||||
= &siSamples[sampleIndex];
|
||||
|
||||
prepared = false; // Prevent any more rendering.
|
||||
if (pCurSample != nullptr) {
|
||||
vkctx.queue.waitIdle(); // Wait for current rendering to finish.
|
||||
delete pCurSample;
|
||||
// Certain events can be triggered during new sample initialization
|
||||
// while pCurSample is not valid, e.g. FOCUS_LOST caused by a Vulkan
|
||||
// validation failure raising a message box. Protect against problems
|
||||
// from this by indicating there is no current sample.
|
||||
pCurSample = nullptr;
|
||||
}
|
||||
|
||||
uint32_t unsupportedTypeExceptions = 0;
|
||||
std::string fileTitle;
|
||||
for (;;) {
|
||||
try {
|
||||
if (infiles.size() > 0) {
|
||||
fileTitle = "Viewing file ";
|
||||
fileTitle += infiles[sampleIndex];
|
||||
pCurSample = showFile(infiles[sampleIndex]);
|
||||
} else {
|
||||
sampleInv = &siSamples[sampleIndex];
|
||||
pCurSample = sampleInv->createSample(vkctx, w_width, w_height,
|
||||
sampleInv->args,
|
||||
sBasePath);
|
||||
}
|
||||
break;
|
||||
} catch (unsupported_ttype& e) {
|
||||
(void)e; // To quiet unused variable warnings from some compilers.
|
||||
unsupportedTypeExceptions++;
|
||||
if (unsupportedTypeExceptions == sampleIndex.getNumSamples()) {
|
||||
std::string manyFailMessage =
|
||||
"All of the specified samples or files use texture types "
|
||||
"or features not supported on this platform.";
|
||||
const std::string& message = unsupportedTypeExceptions == 1 ?
|
||||
e.what() : manyFailMessage;
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR,
|
||||
infiles.size() > 0 ? fileTitle.c_str() : sampleInv->title,
|
||||
message.c_str(),
|
||||
NULL);
|
||||
exit(0);
|
||||
} else {
|
||||
dir == Direction::eForward ? ++sampleIndex : --sampleIndex;
|
||||
}
|
||||
} catch (bad_vulkan_alloc& e) {
|
||||
const SDL_MessageBoxButtonData buttons[] = {
|
||||
/* .flags, .buttonid, .text */
|
||||
{ SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, 0, "Abort" },
|
||||
};
|
||||
const SDL_MessageBoxData messageboxdata = {
|
||||
SDL_MESSAGEBOX_ERROR, // .flags
|
||||
NULL, // .window
|
||||
infiles.size() > 0 ?
|
||||
fileTitle.c_str() : sampleInv->title, // .title
|
||||
e.what(), // .message
|
||||
SDL_arraysize(buttons), // .numbuttons
|
||||
buttons, // .buttons
|
||||
NULL //&colorScheme // .colorScheme
|
||||
};
|
||||
int buttonid;
|
||||
if (!SDL_ShowMessageBox(&messageboxdata, &buttonid)) {
|
||||
SDL_Log("error displaying error message box");
|
||||
exit(1);
|
||||
}
|
||||
if (buttonid == 0) {
|
||||
// We've been told to quit or no button was pressed.
|
||||
exit(1);
|
||||
}
|
||||
} catch (std::exception& e) {
|
||||
const SDL_MessageBoxButtonData buttons[] = {
|
||||
/* .flags, .buttonid, .text */
|
||||
{ SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, 0, "Continue" },
|
||||
{ SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, 1, "Abort" },
|
||||
};
|
||||
#if 0
|
||||
const SDL_MessageBoxColorScheme colorScheme = {
|
||||
{ /* .colors (.r, .g, .b) */
|
||||
/* [SDL_MESSAGEBOX_COLOR_BACKGROUND] */
|
||||
{ 255, 0, 0 },
|
||||
/* [SDL_MESSAGEBOX_COLOR_TEXT] */
|
||||
{ 0, 255, 0 },
|
||||
/* [SDL_MESSAGEBOX_COLOR_BUTTON_BORDER] */
|
||||
{ 255, 255, 0 },
|
||||
/* [SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND] */
|
||||
{ 0, 0, 255 },
|
||||
/* [SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED] */
|
||||
{ 255, 0, 255 }
|
||||
}
|
||||
};
|
||||
#endif
|
||||
const SDL_MessageBoxData messageboxdata = {
|
||||
SDL_MESSAGEBOX_ERROR, // .flags
|
||||
NULL, // .window
|
||||
infiles.size() > 0 ?
|
||||
fileTitle.c_str() : sampleInv->title, // .title
|
||||
e.what(), // .message
|
||||
SDL_arraysize(buttons), // .numbuttons
|
||||
buttons, // .buttons
|
||||
NULL //&colorScheme // .colorScheme
|
||||
};
|
||||
int buttonid;
|
||||
if (!SDL_ShowMessageBox(&messageboxdata, &buttonid)) {
|
||||
SDL_Log("error displaying error message box");
|
||||
exit(1);
|
||||
}
|
||||
if (buttonid == 0) {
|
||||
// Continue
|
||||
dir == Direction::eForward ? ++sampleIndex : --sampleIndex;
|
||||
} else {
|
||||
// We've been told to quit or no button was pressed.
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
prepared = true;
|
||||
setAppTitle(pCurSample->customizeTitle(infiles.size() > 0 ?
|
||||
fileTitle.c_str()
|
||||
: sampleInv->title));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VulkanLoadTests::onFPSUpdate()
|
||||
{
|
||||
VulkanAppSDL::onFPSUpdate();
|
||||
}
|
||||
|
||||
VulkanLoadTestSample*
|
||||
VulkanLoadTests::showFile(const std::string& filename)
|
||||
{
|
||||
KTX_error_code ktxresult;
|
||||
ktxTexture* kTexture;
|
||||
ktxresult = ktxTexture_CreateFromNamedFile(filename.c_str(),
|
||||
KTX_TEXTURE_CREATE_NO_FLAGS,
|
||||
&kTexture);
|
||||
if (KTX_SUCCESS != ktxresult) {
|
||||
std::stringstream message;
|
||||
|
||||
message << "Creation of ktxTexture from \"" << filename
|
||||
<< "\" failed: " << ktxErrorString(ktxresult);
|
||||
throw std::runtime_error(message.str());
|
||||
}
|
||||
|
||||
VulkanLoadTestSample::PFN_create createViewer;
|
||||
VulkanLoadTestSample* pViewer;
|
||||
if (kTexture->numDimensions == 3)
|
||||
createViewer = Texture3d::create;
|
||||
else if (kTexture->isArray && kTexture->isCubemap) {
|
||||
// TODO: Add cubemap array app.
|
||||
std::stringstream message;
|
||||
message << "Display of cubemap array textures not yet implemented.";
|
||||
throw std::runtime_error(message.str());
|
||||
} else if (kTexture->isArray) {
|
||||
createViewer = TextureArray::create;
|
||||
} else if (kTexture->isCubemap) {
|
||||
createViewer = TextureCubemap::create;
|
||||
} else if (kTexture->numLevels > 1) {
|
||||
createViewer = TextureMipmap::create;
|
||||
} else {
|
||||
createViewer = Texture::create;
|
||||
}
|
||||
ktxTexture_Destroy(kTexture);
|
||||
|
||||
// Escape any spaces in filename.
|
||||
std::string args = "--external " + std::regex_replace( filename, std::regex(" "), "\\ " );
|
||||
pViewer = createViewer(vkctx, w_width, w_height, args.c_str(), sBasePath);
|
||||
return pViewer;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
const VulkanLoadTests::sampleInvocation siSamples[] = {
|
||||
{ Texture::create,
|
||||
"etc1s_Iron_Bars_001_normal.ktx2",
|
||||
"Transcode of ETC1S+BasisLZ Compressed KTX2 XY normal map mipmapped"
|
||||
},
|
||||
{ Texture::create,
|
||||
"uastc_Iron_Bars_001_normal.ktx2",
|
||||
"Transcode of UASTC+zstd Compressed KTX2 XY normal map mipmapped"
|
||||
},
|
||||
{ Texture::create,
|
||||
"--use-vma uastc_Iron_Bars_001_normal.ktx2",
|
||||
"Transcode of UASTC+zstd Compressed KTX2 XY normal map mipmapped, using VMA"
|
||||
},
|
||||
{ Texture::create,
|
||||
"ktx_document_uastc_rdo4_zstd5.ktx2",
|
||||
"UASTC+rdo+zstd compressed KTX2 RGBA8 mipmapped"
|
||||
},
|
||||
{ Texture::create,
|
||||
"color_grid_uastc_zstd.ktx2",
|
||||
"UASTC+zstd Compressed KTX2 RGB non-mipmapped"
|
||||
},
|
||||
{ Texture::create,
|
||||
"color_grid_zstd.ktx2",
|
||||
"Zstd Compressed KTX2 RGB non-mipmapped"
|
||||
},
|
||||
{ Texture::create,
|
||||
"color_grid_uastc.ktx2",
|
||||
"UASTC Compressed KTX2 RGB non-mipmapped"
|
||||
},
|
||||
{ Texture::create,
|
||||
"color_grid_basis.ktx2",
|
||||
"ETC1S+BasisLZ Compressed KTX2 RGB non-mipmapped"
|
||||
},
|
||||
{ Texture::create,
|
||||
"kodim17_basis.ktx2",
|
||||
"ETC1S+BasisLZ Compressed KTX2 RGB non-mipmapped"
|
||||
},
|
||||
{ Texture::create,
|
||||
"--qcolor 0.0,0.0,0.0 pattern_02_bc2.ktx2",
|
||||
"KTX2: BC2 (S3TC DXT3) Compressed 2D"
|
||||
},
|
||||
{ TextureMipmap::create,
|
||||
"ktx_document_basis.ktx2",
|
||||
"ETC1S+BasisLZ compressed RGBA + Mipmap"
|
||||
},
|
||||
{ TextureMipmap::create,
|
||||
"rgba-mipmap-reference-basis.ktx2",
|
||||
// ETC1S encoder removed all-1 alpha from rgba-mipmap-reference.
|
||||
"ETC1S+BasisLZ Compressed RGB + Mipmap"
|
||||
},
|
||||
{ Texture3d::create,
|
||||
"3dtex_7_reference_u.ktx2",
|
||||
"RGBA8 3d Texture, Depth == 7"
|
||||
},
|
||||
{ TextureArray::create,
|
||||
"arraytex_7_mipmap_reference_u.ktx2",
|
||||
"RGBA8 Array Texture, Layers = 7"
|
||||
},
|
||||
{ TextureCubemap::create,
|
||||
"cubemap_goldengate_uastc_rdo4_zstd5_rd.ktx2",
|
||||
"UASTC+rdo+zstd Compressed RGB Cube Map"
|
||||
},
|
||||
{ TextureCubemap::create,
|
||||
"--preload cubemap_goldengate_uastc_rdo4_zstd5_rd.ktx2",
|
||||
"UASTC+rdo+zstd Compressed RGB Cube Map from pre-loaded images"
|
||||
},
|
||||
{ TextureCubemap::create,
|
||||
"cubemap_yokohama_basis_rd.ktx2",
|
||||
"ETC1S+BasisLZ Compressed RGB Cube Map"
|
||||
},
|
||||
{ TextureCubemap::create,
|
||||
"--preload cubemap_yokohama_basis_rd.ktx2",
|
||||
"ETC1S+BasisLZ Compressed RGB Cube Map from pre-loaded images"
|
||||
},
|
||||
{ TextureCubemap::create,
|
||||
"skybox_zstd.ktx2",
|
||||
"Zstd Compressed B10G11R11_UFLOAT Cube Map. Tests for correct blockSizeInBits after inflation"
|
||||
},
|
||||
{ Texture::create,
|
||||
"orient-down-metadata.ktx",
|
||||
"RGB8 2D + KTXOrientation down"
|
||||
},
|
||||
{ Texture::create,
|
||||
"orient-up-metadata.ktx",
|
||||
"RGB8 2D + KTXOrientation up"
|
||||
},
|
||||
{ Texture::create,
|
||||
"--linear-tiling orient-up-metadata.ktx",
|
||||
"RGB8 2D + KTXOrientation up with Linear Tiling"
|
||||
},
|
||||
{ Texture::create,
|
||||
"rgba-reference.ktx",
|
||||
"RGBA8 2D"
|
||||
},
|
||||
{ Texture::create,
|
||||
"--linear-tiling rgba-reference.ktx",
|
||||
"RGBA8 2D using Linear Tiling"
|
||||
},
|
||||
{ Texture::create,
|
||||
"etc2-rgb.ktx",
|
||||
"ETC2 RGB8"
|
||||
},
|
||||
{ Texture::create,
|
||||
"etc2-rgba8.ktx",
|
||||
"ETC2 RGB8A8"
|
||||
},
|
||||
{ Texture::create,
|
||||
"etc2-sRGB.ktx",
|
||||
"ETC2 sRGB8"
|
||||
},
|
||||
{ Texture::create,
|
||||
"etc2-sRGBa8.ktx",
|
||||
"ETC2 sRGB8a8"
|
||||
},
|
||||
{ Texture::create,
|
||||
"--qcolor 0.0,0.0,0.0 pattern_02_bc2.ktx",
|
||||
"BC2 (S3TC DXT3) Compressed 2D"
|
||||
},
|
||||
{ TextureMipmap::create,
|
||||
"rgb-amg-reference.ktx",
|
||||
"RGB8 + Auto Mipmap"
|
||||
},
|
||||
{ TextureMipmap::create,
|
||||
"--linear-tiling rgb-amg-reference.ktx",
|
||||
"RGB8 + Auto Mipmap using Linear Tiling"
|
||||
},
|
||||
{ TextureMipmap::create,
|
||||
"metalplate-amg-rgba8.ktx",
|
||||
"RGBA8 2D + Auto Mipmap"
|
||||
},
|
||||
{ TextureMipmap::create,
|
||||
"--linear-tiling metalplate-amg-rgba8.ktx",
|
||||
"RGBA8 2D + Auto Mipmap using Linear Tiling"
|
||||
},
|
||||
{ TextureMipmap::create,
|
||||
"not4_rgb888_srgb.ktx",
|
||||
"RGB8 2D, Row length not Multiple of 4"
|
||||
},
|
||||
{ TextureMipmap::create,
|
||||
"--linear-tiling not4_rgb888_srgb.ktx",
|
||||
"RGB8 2D, Row length not Multiple of 4 using Linear Tiling"
|
||||
},
|
||||
{ TextureArray::create,
|
||||
"texturearray_bc3_unorm.ktx",
|
||||
"BC2 (S3TC DXT3) Compressed Texture Array"
|
||||
},
|
||||
{ TextureArray::create,
|
||||
"--linear-tiling texturearray_bc3_unorm.ktx",
|
||||
"BC2 (S3TC DXT3) Compressed Texture Array using Linear Tiling"
|
||||
},
|
||||
{ TextureArray::create,
|
||||
"texturearray_astc_8x8_unorm.ktx",
|
||||
"ASTC 8x8 Compressed Texture Array"
|
||||
},
|
||||
{ TextureArray::create,
|
||||
"texturearray_etc2_unorm.ktx",
|
||||
"ETC2 Compressed Texture Array"
|
||||
},
|
||||
#if 0
|
||||
{ TexturedCube::create,
|
||||
"rgb-amg-reference.ktx",
|
||||
"RGB8 + Auto Mipmap"
|
||||
},
|
||||
{ TexturedCube::create,
|
||||
"rgb-amg-reference.ktx",
|
||||
"RGB8 + Auto Mipmap"
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
||||
const uint32_t uNumSamples = sizeof(siSamples) / sizeof(VulkanLoadTests::sampleInvocation);
|
||||
|
||||
AppBaseSDL* theApp = new VulkanLoadTests(siSamples, uNumSamples,
|
||||
"KTX Loader Tests for Vulkan");
|
||||
@@ -0,0 +1,89 @@
|
||||
/* -*- tab-width: 4; -*- */
|
||||
/* vi: set sw=2 ts=4 expandtab: */
|
||||
|
||||
#ifndef VULKAN_LOAD_TESTS_H
|
||||
#define VULKAN_LOAD_TESTS_H
|
||||
|
||||
/*
|
||||
* Copyright 2017-2020 Mark Callow.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "VulkanAppSDL.h"
|
||||
#include "utils/VulkanMeshLoader.hpp"
|
||||
#include "SwipeDetector.h"
|
||||
#include "VulkanLoadTestSample.h"
|
||||
#include <string>
|
||||
|
||||
class VulkanLoadTests : public VulkanAppSDL {
|
||||
public:
|
||||
/** A table of samples and arguments */
|
||||
typedef struct sampleInvocation_ {
|
||||
const VulkanLoadTestSample::PFN_create createSample;
|
||||
const char* const args;
|
||||
const char* const title;
|
||||
} sampleInvocation;
|
||||
|
||||
VulkanLoadTests(const sampleInvocation samples[],
|
||||
const uint32_t numSamples,
|
||||
const char* const name);
|
||||
virtual ~VulkanLoadTests();
|
||||
virtual bool doEvent(SDL_Event* event);
|
||||
virtual void drawFrame(uint32_t msTicks);
|
||||
virtual void finalize();
|
||||
virtual void getOverlayText(float yOffset);
|
||||
virtual bool initialize(Args& args);
|
||||
virtual void onFPSUpdate();
|
||||
virtual void windowResized();
|
||||
|
||||
protected:
|
||||
enum class Direction {
|
||||
eForward,
|
||||
eBack
|
||||
};
|
||||
void invokeSample(Direction dir);
|
||||
VulkanLoadTestSample* showFile(const std::string& filename);
|
||||
VulkanLoadTestSample* pCurSample;
|
||||
|
||||
bool quit = false;
|
||||
|
||||
const sampleInvocation* const siSamples;
|
||||
class sampleIndex {
|
||||
public:
|
||||
sampleIndex(const int32_t numSamples) : numSamples(numSamples) {
|
||||
index = 0;
|
||||
}
|
||||
sampleIndex& operator++() {
|
||||
if (++index >= numSamples)
|
||||
index = 0;
|
||||
return *this;
|
||||
}
|
||||
sampleIndex& operator--() {
|
||||
if (--index > numSamples /* underflow */)
|
||||
index = numSamples-1;
|
||||
return *this;
|
||||
}
|
||||
operator int32_t() {
|
||||
return index;
|
||||
}
|
||||
uint32_t getNumSamples() { return numSamples; }
|
||||
void setNumSamples(uint32_t ns) { numSamples = ns; }
|
||||
|
||||
protected:
|
||||
uint32_t numSamples;
|
||||
uint32_t index;
|
||||
} sampleIndex;
|
||||
|
||||
std::vector<std::string> infiles;
|
||||
|
||||
Sint64 dropCompleteTime = 0;
|
||||
struct {
|
||||
int32_t x;
|
||||
int32_t y;
|
||||
Sint64 timestamp;
|
||||
} buttonDown;
|
||||
|
||||
SwipeDetector swipeDetector;
|
||||
};
|
||||
|
||||
#endif /* VULKAN_LOAD_TESTS_H */
|
||||
@@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Copyright 2015-2024 Mark Callow
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
WARNING: Do not attempt to edit the Info.plist you see in the Xcode
|
||||
project. It is unavoidably configured from this by CMake. Edit here.
|
||||
-->
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleName</key>
|
||||
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
|
||||
<key>CFBundleGetInfoString</key>
|
||||
<string>${MACOSX_BUNDLE_INFO_STRING}</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>NSMainNibFile</key>
|
||||
<string></string>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<string>YES</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"orientation" : "portrait",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "launch-portrait-iphone@x2.png",
|
||||
"extent" : "full-screen",
|
||||
"minimum-system-version" : "7.0",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"extent" : "full-screen",
|
||||
"idiom" : "iphone",
|
||||
"subtype" : "retina4",
|
||||
"filename" : "launch-portrait-iphone@r4.png",
|
||||
"minimum-system-version" : "7.0",
|
||||
"orientation" : "portrait",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"orientation" : "portrait",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "launch-portrait@x1.png",
|
||||
"extent" : "full-screen",
|
||||
"minimum-system-version" : "7.0",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"orientation" : "landscape",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "launch-landscape@x1.png",
|
||||
"extent" : "full-screen",
|
||||
"minimum-system-version" : "7.0",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"orientation" : "portrait",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "launch-portrait@2x.png",
|
||||
"extent" : "full-screen",
|
||||
"minimum-system-version" : "7.0",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"orientation" : "landscape",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "launch-landscape@x2.png",
|
||||
"extent" : "full-screen",
|
||||
"minimum-system-version" : "7.0",
|
||||
"scale" : "2x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 41 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 42 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
@@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Copyright 2017-2020 The Khronos Group Inc.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
||||
<device id="retina6_12" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21678"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="EHf-IW-A2E">
|
||||
<objects>
|
||||
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
||||
<layoutGuides>
|
||||
<viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
|
||||
<viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
|
||||
</layoutGuides>
|
||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" textAlignment="center" lineBreakMode="middleTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="GJd-Yh-RWb">
|
||||
<rect key="frame" x="20" y="300" width="560" height="86"/>
|
||||
<string key="text">KTX Texture
|
||||
Vulkan Loader Tests</string>
|
||||
<fontDescription key="fontDescription" name="HelveticaNeue" family="Helvetica Neue" pointSize="36"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<color key="shadowColor" red="0.66666668653488159" green="0.66666668653488159" blue="0.66666668653488159" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<size key="shadowOffset" width="1" height="2"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="GJd-Yh-RWb" firstAttribute="top" secondItem="Llm-lL-Icb" secondAttribute="bottom" constant="280" id="9aC-lF-eik"/>
|
||||
<constraint firstItem="GJd-Yh-RWb" firstAttribute="trailing" secondItem="Ze5-6b-2t3" secondAttribute="trailingMargin" id="owI-kB-NeP"/>
|
||||
<constraint firstItem="GJd-Yh-RWb" firstAttribute="leading" secondItem="Ze5-6b-2t3" secondAttribute="leadingMargin" id="s5e-cW-zgl"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="53" y="375"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
@@ -0,0 +1,28 @@
|
||||
# Copyright 2023 Khronos Group, Inc.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# Feel free to edit this file. After editing you MUST run cmake project
|
||||
# configuration again to generate updated output files before building.
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Name=Vulkan Load Tests
|
||||
Name[ja]=Vulkan Load Tests
|
||||
Name[fr]=Vulkan Load Tests
|
||||
GenericName=KTX loader tests and file viewer
|
||||
GenericName[ja]=KTXローダーテストとファイルビューア
|
||||
GenericName[fr]=Tests de chargeur KTX et visionneuse de fichiers
|
||||
Comment=View KTX files using Vulkan or run VkUpload functionality tests.
|
||||
Comment[fr]=Affichez les fichiers KTX à l'aide d'Vulkan ou exécutez des tests de fonctionnalité VkUpload.
|
||||
Comment[ja]=Vulkanを使用してKTXファイルを表示するか、VkUpload機能テストを実行します。
|
||||
TryExec=vkloadtests
|
||||
Exec=vkloadtests %F
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Keywords=Ktx;Texture;Viewer
|
||||
Keywords[ja]=Ktx;Texture;Viewer
|
||||
Keywords[fr]=Ktx;Texture;Viewer
|
||||
Icon=/opt/vkloadtests/resources/ktx_app.svg
|
||||
Categories=Utility;
|
||||
StartupNotify=true
|
||||
MimeType=image/ktx;image/ktx2;
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<!--
|
||||
Copyright 2015-2024 Mark Callow
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
WARNING: Do not attempt to edit the Info.plist you see in the Xcode
|
||||
project. It is unavoidably configured from this by CMake. Edit here.
|
||||
-->
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleName</key>
|
||||
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
|
||||
<key>CFBundleGetInfoString</key>
|
||||
<string>${MACOSX_BUNDLE_INFO_STRING}</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>${MACOSX_BUNDLE_ICON_FILE}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>LSIsAppleDefaultForType</key>
|
||||
<!-- An already installed "Default" handler wins over this
|
||||
unless this is set to true. Leave it false and let user
|
||||
choose. -->
|
||||
<false/>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Default</string>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>${KTX_DOC_ICON}</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>public.ktx2</string>
|
||||
<string>public.ktx</string>
|
||||
</array>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>ktx</string>
|
||||
<string>ktx2</string>
|
||||
</array>
|
||||
<key>CFBundleTypeMIMETypes</key>
|
||||
<array>
|
||||
<string>image/ktx</string>
|
||||
<string>image/ktx2</string>
|
||||
</array>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>KTX texture file</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSTypeIsPackage</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</array>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.graphics-design</string>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<true/>
|
||||
<key>NSMainNibFile</key>
|
||||
<string></string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array/>
|
||||
</dict>
|
||||
</plist>
|
||||
Binary file not shown.
@@ -0,0 +1,77 @@
|
||||
// Copyright 2017 Mark Callow
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Fragment shader for single texture
|
||||
|
||||
#version 450
|
||||
|
||||
#extension GL_ARB_separate_shader_objects : enable
|
||||
#extension GL_ARB_shading_language_420pack : enable
|
||||
|
||||
#if 0
|
||||
//layout (binding = 1) uniform sampler2D samplerColor;
|
||||
|
||||
layout (location = 0) in vec2 inUV;
|
||||
layout (location = 1) in vec4 inColor;
|
||||
#if 0
|
||||
layout (location = 2) in float inLodBias;
|
||||
layout (location = 3) in vec3 inNormal;
|
||||
layout (location = 4) in vec3 inViewVec;
|
||||
layout (location = 5) in vec3 inLightVec;
|
||||
#endif
|
||||
|
||||
layout (location = 0) out vec4 outFragColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
#if 0
|
||||
vec4 color = texture(samplerColor, inUV, inLodBias);
|
||||
|
||||
vec3 N = normalize(inNormal);
|
||||
vec3 L = normalize(inLightVec);
|
||||
vec3 V = normalize(inViewVec);
|
||||
vec3 R = reflect(-L, N);
|
||||
vec3 diffuse = max(dot(N, L), 0.0) * vec3(1.0);
|
||||
float specular = pow(max(dot(R, V), 0.0), 16.0) * color.a;
|
||||
|
||||
outFragColor = vec4(diffuse * color.rgb + specular, 1.0);
|
||||
#endif
|
||||
outFragColor = vec4(inColor.rgb, 1.0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
//layout (binding = 1) uniform sampler2D samplerColor;
|
||||
|
||||
layout (location = 0) in vec2 inUV;
|
||||
layout (location = 1) in float inLodBias;
|
||||
layout (location = 2) in vec3 inNormal;
|
||||
layout (location = 3) in vec3 inViewVec;
|
||||
layout (location = 4) in vec3 inLightVec;
|
||||
|
||||
layout (location = 0) out vec4 outFragColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
//vec4 color = texture(samplerColor, inUV, inLodBias);
|
||||
vec4 color = vec4(1.0, 0.0, 0.0, 1.0);
|
||||
|
||||
vec3 N = normalize(inNormal);
|
||||
vec3 L = normalize(inLightVec);
|
||||
vec3 V = normalize(inViewVec);
|
||||
vec3 R = reflect(-L, N);
|
||||
vec3 diffuse = max(dot(N, L), 0.0) * vec3(1.0);
|
||||
float specular = pow(max(dot(R, V), 0.0), 16.0) * color.a;
|
||||
|
||||
outFragColor = vec4(diffuse * color.rgb + specular, 1.0);
|
||||
}
|
||||
#endif
|
||||
|
||||
//layout (location = 0) in vec4 color;
|
||||
layout (location = 0) out vec4 outColor;
|
||||
|
||||
void main() {
|
||||
//outColor = color;
|
||||
outColor = vec4(1.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
// Copyright 2016 Mark Callow
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Vertex shader for cube sample.
|
||||
|
||||
#version 450
|
||||
|
||||
#extension GL_ARB_separate_shader_objects : enable
|
||||
#extension GL_ARB_shading_language_420pack : enable
|
||||
|
||||
#if 0
|
||||
layout (location = 0) in vec3 inPos;
|
||||
layout (location = 1) in vec3 inNormal;
|
||||
layout (location = 2) in vec2 inUV;
|
||||
layout (location = 3) in vec4 inColor;
|
||||
|
||||
layout (binding = 0) uniform UBO
|
||||
{
|
||||
mat4 projection;
|
||||
mat4 model;
|
||||
vec4 viewPos;
|
||||
float lodBias;
|
||||
} ubo;
|
||||
|
||||
layout (location = 0) out vec2 outUV;
|
||||
layout (location = 1) out vec4 outColor;
|
||||
//layout (location = 1) out float outLodBias;
|
||||
//layout (location = 2) out vec3 outNormal;
|
||||
//layout (location = 3) out vec3 outViewVec;
|
||||
//layout (location = 4) out vec3 outLightVec;
|
||||
|
||||
out gl_PerVertex
|
||||
{
|
||||
vec4 gl_Position;
|
||||
};
|
||||
|
||||
void main()
|
||||
{
|
||||
outUV = inUV;
|
||||
outColor = inColor;
|
||||
//outLodBias = ubo.lodBias;
|
||||
|
||||
//vec3 worldPos = vec3(ubo.model * vec4(inPos, 1.0));
|
||||
|
||||
gl_Position = ubo.projection * ubo.model * vec4(inPos.xyz, 1.0);
|
||||
|
||||
#if 0
|
||||
vec4 pos = ubo.model * vec4(inPos, 1.0);
|
||||
outNormal = mat3(inverse(transpose(ubo.model))) * inNormal;
|
||||
vec3 lightPos = vec3(0.0);
|
||||
vec3 lPos = mat3(ubo.model) * lightPos.xyz;
|
||||
outLightVec = lPos - pos.xyz;
|
||||
outViewVec = ubo.viewPos.xyz - pos.xyz;
|
||||
#endif
|
||||
|
||||
// GL->VK conventions. Sigh!
|
||||
//gl_Position.y = -gl_Position.y;
|
||||
//gl_Position.z = (gl_Position.z + gl_Position.w) / 2.0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
layout (location = 0) in vec3 inPos;
|
||||
layout (location = 1) in vec2 inUV;
|
||||
layout (location = 2) in vec3 inNormal;
|
||||
|
||||
layout (binding = 0) uniform UBO
|
||||
{
|
||||
mat4 projection;
|
||||
mat4 model;
|
||||
vec4 viewPos;
|
||||
float lodBias;
|
||||
} ubo;
|
||||
|
||||
layout (location = 0) out vec2 outUV;
|
||||
layout (location = 1) out float outLodBias;
|
||||
layout (location = 2) out vec3 outNormal;
|
||||
layout (location = 3) out vec3 outViewVec;
|
||||
layout (location = 4) out vec3 outLightVec;
|
||||
|
||||
out gl_PerVertex
|
||||
{
|
||||
vec4 gl_Position;
|
||||
};
|
||||
|
||||
void main()
|
||||
{
|
||||
outUV = inUV;
|
||||
outLodBias = ubo.lodBias;
|
||||
|
||||
vec3 worldPos = vec3(ubo.model * vec4(inPos, 1.0));
|
||||
|
||||
gl_Position = ubo.projection * ubo.model * vec4(inPos.xyz, 1.0);
|
||||
|
||||
vec4 pos = ubo.model * vec4(inPos, 1.0);
|
||||
outNormal = mat3(inverse(transpose(ubo.model))) * inNormal;
|
||||
vec3 lightPos = vec3(0.0);
|
||||
vec3 lPos = mat3(ubo.model) * lightPos.xyz;
|
||||
outLightVec = lPos - pos.xyz;
|
||||
outViewVec = ubo.viewPos.xyz - pos.xyz;
|
||||
}
|
||||
#endif
|
||||
|
||||
layout (location = 0) in vec3 inPos;
|
||||
// layout (location = 3) in vec4 inColor;
|
||||
// layout (location = 0) out vec4 outColor;
|
||||
|
||||
layout (std140, binding = 0) uniform UBO
|
||||
{
|
||||
mat4 projection;
|
||||
mat4 model;
|
||||
mat4 view;
|
||||
vec4 viewPos;
|
||||
float lodBias;
|
||||
} ubo;
|
||||
|
||||
void main() {
|
||||
//outColor = inColor;r
|
||||
mat4 mvp = ubo.projection * ubo.view * ubo.model;
|
||||
gl_Position = mvp * vec4(inPos, 1.0);
|
||||
//gl_Position = vec4(inPos * 0.5, 1.0);
|
||||
//gl_PointSize = 2.0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright 2017 Mark Callow
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#version 450
|
||||
|
||||
#extension GL_ARB_separate_shader_objects : enable
|
||||
#extension GL_ARB_shading_language_420pack : enable
|
||||
|
||||
layout (binding = 0) uniform UBO
|
||||
{
|
||||
mat4 projection;
|
||||
mat4 modelView;
|
||||
mat4 invModelView;
|
||||
mat4 uvwTransform;
|
||||
float lodBias;
|
||||
} ubo;
|
||||
|
||||
layout (binding = 1) uniform samplerCube samplerColor;
|
||||
|
||||
layout (location = 0) in vec3 inPos;
|
||||
layout (location = 1) in vec3 inNormal;
|
||||
layout (location = 2) in float inLodBias;
|
||||
layout (location = 3) in vec3 inViewVec;
|
||||
layout (location = 4) in vec3 inLightVec;
|
||||
|
||||
layout (location = 0) out vec4 outFragColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec3 cI = normalize (inPos);
|
||||
vec3 cR = reflect (cI, normalize(inNormal));
|
||||
|
||||
//cR = vec3(ubo.invModelView * vec4(cR, 0.0));
|
||||
cR = vec3(ubo.uvwTransform * ubo.invModelView * vec4(cR, 0.0));
|
||||
|
||||
vec4 color = texture(samplerColor, cR, inLodBias);
|
||||
|
||||
vec3 N = normalize(inNormal);
|
||||
vec3 L = normalize(inLightVec);
|
||||
vec3 V = normalize(inViewVec);
|
||||
vec3 R = reflect(-L, N);
|
||||
vec3 ambient = vec3(0.5) * color.rgb;
|
||||
vec3 diffuse = max(dot(N, L), 0.0) * vec3(1.0);
|
||||
vec3 specular = pow(max(dot(R, V), 0.0), 16.0) * vec3(0.5);
|
||||
outFragColor = vec4(ambient + diffuse * color.rgb + specular, 1.0);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright 2017 Mark Callow
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#version 450
|
||||
|
||||
#extension GL_ARB_separate_shader_objects : enable
|
||||
#extension GL_ARB_shading_language_420pack : enable
|
||||
|
||||
layout (location = 0) in vec3 inPos;
|
||||
layout (location = 1) in vec3 inNormal;
|
||||
|
||||
layout (binding = 0) uniform UBO
|
||||
{
|
||||
mat4 projection;
|
||||
mat4 modelView;
|
||||
mat4 invModelView;
|
||||
mat4 uvwTransform;
|
||||
float lodBias;
|
||||
} ubo;
|
||||
|
||||
layout (location = 0) out vec3 outPos;
|
||||
layout (location = 1) out vec3 outNormal;
|
||||
layout (location = 2) out float outLodBias;
|
||||
layout (location = 3) out vec3 outViewVec;
|
||||
layout (location = 4) out vec3 outLightVec;
|
||||
|
||||
out gl_PerVertex
|
||||
{
|
||||
vec4 gl_Position;
|
||||
};
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = ubo.projection * ubo.modelView * vec4(inPos.xyz, 1.0);
|
||||
|
||||
outPos = vec3(ubo.modelView * vec4(inPos, 1.0));
|
||||
outNormal = mat3(ubo.modelView) * inNormal;
|
||||
outLodBias = ubo.lodBias;
|
||||
|
||||
vec3 lightPos = vec3(0.0f, -5.0f, 5.0f);
|
||||
outLightVec = lightPos.xyz - outPos.xyz;
|
||||
outViewVec = -outPos.xyz;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright 2017 Mark Callow
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#version 450
|
||||
|
||||
#extension GL_ARB_separate_shader_objects : enable
|
||||
#extension GL_ARB_shading_language_420pack : enable
|
||||
|
||||
layout (binding = 1) uniform samplerCube samplerCubeMap;
|
||||
|
||||
layout (location = 0) in vec3 inUVW;
|
||||
|
||||
layout (location = 0) out vec4 outFragColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
outFragColor = texture(samplerCubeMap, inUVW);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright 2017 Mark Callow
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#version 450
|
||||
|
||||
#extension GL_ARB_separate_shader_objects : enable
|
||||
#extension GL_ARB_shading_language_420pack : enable
|
||||
|
||||
layout (location = 0) in vec3 inPos;
|
||||
|
||||
layout (binding = 0) uniform UBO
|
||||
{
|
||||
mat4 projection;
|
||||
mat4 modelView;
|
||||
mat4 invModelView;
|
||||
mat4 uvwTransform;
|
||||
} ubo;
|
||||
|
||||
layout (location = 0) out vec3 outUVW;
|
||||
|
||||
out gl_PerVertex
|
||||
{
|
||||
vec4 gl_Position;
|
||||
};
|
||||
|
||||
void main()
|
||||
{
|
||||
//outUVW = inPos.xyz;
|
||||
outUVW = (ubo.uvwTransform * vec4(inPos.xyz, 1.0)).xyz;
|
||||
// Override the Z component with the W component to guarantee that the
|
||||
// final Z value of the position will be 1.0.
|
||||
gl_Position = (ubo.projection * ubo.modelView * vec4(inPos.xyz, 1.0)).xyww;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// Copyright 2017 Mark Callow
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#version 450
|
||||
|
||||
#extension GL_ARB_separate_shader_objects : enable
|
||||
#extension GL_ARB_shading_language_420pack : enable
|
||||
|
||||
layout (location = 0) in vec3 inPos;
|
||||
layout (location = 1) in vec2 inUV;
|
||||
layout (location = 2) in vec3 inNormal;
|
||||
layout (location = 3) in vec3 inColor;
|
||||
|
||||
layout (binding = 0) uniform UBO
|
||||
{
|
||||
mat4 projection;
|
||||
mat4 model;
|
||||
mat3 normal;
|
||||
vec4 viewPos;
|
||||
float lodBias;
|
||||
} ubo;
|
||||
|
||||
layout (location = 0) out vec2 outUV;
|
||||
layout (location = 1) out float outLodBias;
|
||||
layout (location = 2) out vec3 outNormal;
|
||||
layout (location = 3) out vec3 outViewVec;
|
||||
layout (location = 4) out vec3 outLightVec;
|
||||
layout (location = 5) out vec3 outColor;
|
||||
|
||||
out gl_PerVertex
|
||||
{
|
||||
vec4 gl_Position;
|
||||
};
|
||||
|
||||
void main()
|
||||
{
|
||||
outUV = inUV;
|
||||
outLodBias = ubo.lodBias;
|
||||
outColor = inColor;
|
||||
|
||||
//vec3 worldPos = vec3(ubo.model * vec4(inPos, 1.0));
|
||||
|
||||
gl_Position = ubo.projection * ubo.model * vec4(inPos.xyz, 1.0);
|
||||
|
||||
vec4 pos = ubo.model * vec4(inPos, 1.0);
|
||||
//outNormal = mat3(inverse(transpose(ubo.model))) * inNormal;
|
||||
outNormal = mat3(ubo.normal) * inNormal;
|
||||
vec3 lightPos = vec3(0.0);
|
||||
vec3 lPos = mat3(ubo.model) * lightPos.xyz;
|
||||
outLightVec = lPos - pos.xyz;
|
||||
outViewVec = ubo.viewPos.xyz - pos.xyz;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright 2017 Mark Callow
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#version 450
|
||||
|
||||
#extension GL_ARB_separate_shader_objects : enable
|
||||
#extension GL_ARB_shading_language_420pack : enable
|
||||
|
||||
layout (binding = 1) uniform sampler1D samplerColor;
|
||||
|
||||
layout (location = 0) in vec2 inUV;
|
||||
layout (location = 1) in float inLodBias;
|
||||
layout (location = 2) in vec3 inNormal;
|
||||
layout (location = 3) in vec3 inViewVec;
|
||||
layout (location = 4) in vec3 inLightVec;
|
||||
layout (location = 5) in vec3 inColor;
|
||||
|
||||
layout (location = 0) out vec4 outFragColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 fragcolor, texcolor;
|
||||
|
||||
texcolor = texture(samplerColor, inUV.x, inLodBias);
|
||||
fragcolor.rgb = inColor.rgb * (1.0f - texcolor.a) + texcolor.rgb * texcolor.a;
|
||||
fragcolor.a = texcolor.a;
|
||||
|
||||
vec3 N = normalize(inNormal);
|
||||
vec3 L = normalize(inLightVec);
|
||||
vec3 V = normalize(inViewVec);
|
||||
vec3 R = reflect(-L, N);
|
||||
vec3 diffuse = max(dot(N, L), 0.0) * vec3(1.0);
|
||||
float specular = pow(max(dot(R, V), 0.0), 16.0) * fragcolor.a;
|
||||
|
||||
outFragColor = vec4(diffuse * fragcolor.rgb + specular, 1.0);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright 2017 Mark Callow
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#version 450
|
||||
|
||||
#extension GL_ARB_separate_shader_objects : enable
|
||||
#extension GL_ARB_shading_language_420pack : enable
|
||||
|
||||
layout (binding = 1) uniform sampler2D samplerColor;
|
||||
|
||||
layout (location = 0) in vec2 inUV;
|
||||
layout (location = 1) in float inLodBias;
|
||||
layout (location = 2) in vec3 inNormal;
|
||||
layout (location = 3) in vec3 inViewVec;
|
||||
layout (location = 4) in vec3 inLightVec;
|
||||
layout (location = 5) in vec3 inColor;
|
||||
|
||||
layout (location = 0) out vec4 outFragColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 fragcolor, texcolor;
|
||||
|
||||
texcolor = texture(samplerColor, inUV, inLodBias);
|
||||
fragcolor.rgb = inColor.rgb * (1.0f - texcolor.a) + texcolor.rgb * texcolor.a;
|
||||
fragcolor.a = texcolor.a;
|
||||
|
||||
vec3 N = normalize(inNormal);
|
||||
vec3 L = normalize(inLightVec);
|
||||
vec3 V = normalize(inViewVec);
|
||||
vec3 R = reflect(-L, N);
|
||||
vec3 diffuse = max(dot(N, L), 0.0) * vec3(1.0);
|
||||
float specular = pow(max(dot(R, V), 0.0), 16.0) * fragcolor.a;
|
||||
|
||||
outFragColor = vec4(diffuse * fragcolor.rgb + specular, 1.0);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright 2017 Mark Callow
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#version 450
|
||||
|
||||
#extension GL_ARB_separate_shader_objects : enable
|
||||
#extension GL_ARB_shading_language_420pack : enable
|
||||
|
||||
layout (binding = 1) uniform sampler3D sampler3d;
|
||||
|
||||
layout (location = 0) in vec3 inUVW;
|
||||
|
||||
layout (location = 0) out vec4 outFragColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
outFragColor = texture(sampler3d, inUVW);
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright 2017 Mark Callow
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#version 450
|
||||
|
||||
#extension GL_ARB_separate_shader_objects : enable
|
||||
#extension GL_ARB_shading_language_420pack : enable
|
||||
|
||||
layout (location = 0) in vec4 inPos;
|
||||
layout (location = 1) in vec2 inUV;
|
||||
|
||||
struct Instance
|
||||
{
|
||||
mat4 model;
|
||||
};
|
||||
|
||||
// Keep the default value small to avoid MoltenVK issue 1420.
|
||||
// https://github.com/KhronosGroup/MoltenVK/issues/1420
|
||||
//layout(constant_id = 1) const int instanceCount = 1;
|
||||
// Sadly the above does not work on iOS, only macOS so declare
|
||||
// roughly the max size expected and in the app allocate the size
|
||||
// as declared.
|
||||
layout(constant_id = 1) const int instanceCount = 30;
|
||||
|
||||
layout (binding = 0, std140) uniform UBO
|
||||
{
|
||||
mat4 projection;
|
||||
mat4 view;
|
||||
Instance instance[instanceCount];
|
||||
} ubo;
|
||||
|
||||
// Workaround MoltenVK issue 1421 by passing instanceCount as a push constant.
|
||||
// https://github.com/KhronosGroup/MoltenVK/issues/1421.
|
||||
layout(push_constant) uniform PushConstants
|
||||
{
|
||||
uint instanceCount;
|
||||
} constants;
|
||||
|
||||
layout (location = 0) out vec3 outUVW;
|
||||
|
||||
void main()
|
||||
{
|
||||
float divisor = max(1.0, constants.instanceCount - 1);
|
||||
//float divisor = max(1.0, instanceCount - 1);
|
||||
outUVW = vec3(inUV, gl_InstanceIndex / divisor);
|
||||
mat4 modelView = ubo.view * ubo.instance[gl_InstanceIndex].model;
|
||||
gl_Position = ubo.projection * modelView * inPos;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright 2017 Mark Callow
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#version 450
|
||||
|
||||
#extension GL_ARB_separate_shader_objects : enable
|
||||
#extension GL_ARB_shading_language_420pack : enable
|
||||
|
||||
layout (binding = 1) uniform sampler2DArray samplerArray;
|
||||
|
||||
layout (location = 0) in vec3 inUVW;
|
||||
|
||||
layout (location = 0) out vec4 outFragColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
outFragColor = texture(samplerArray, inUVW);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright 2017 Mark Callow
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#version 450
|
||||
|
||||
#extension GL_ARB_separate_shader_objects : enable
|
||||
#extension GL_ARB_shading_language_420pack : enable
|
||||
|
||||
layout (location = 0) in vec4 inPos;
|
||||
layout (location = 1) in vec2 inUV;
|
||||
|
||||
struct Instance
|
||||
{
|
||||
mat4 model;
|
||||
};
|
||||
|
||||
// Keep the default value small to avoid MoltenVK issue 1420.
|
||||
// https://github.com/KhronosGroup/MoltenVK/issues/1420
|
||||
//layout(constant_id = 1) const int instanceCount = 1;
|
||||
// Sadly the above does not work on iOS, only macOS so declare
|
||||
// roughly the max size expected and in the app allocate the size
|
||||
// as declared.
|
||||
layout(constant_id = 1) const int instanceCount = 30;
|
||||
|
||||
layout (binding = 0, std140) uniform UBO
|
||||
{
|
||||
mat4 projection;
|
||||
mat4 view;
|
||||
Instance instance[instanceCount];
|
||||
} ubo;
|
||||
|
||||
layout (location = 0) out vec3 outUVW;
|
||||
|
||||
void main()
|
||||
{
|
||||
outUVW = vec3(inUV, gl_InstanceIndex);
|
||||
mat4 modelView = ubo.view * ubo.instance[gl_InstanceIndex].model;
|
||||
gl_Position = ubo.projection * modelView * inPos;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// Copyright 2017 Mark Callow
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#version 450
|
||||
|
||||
#extension GL_ARB_separate_shader_objects : enable
|
||||
#extension GL_ARB_shading_language_420pack : enable
|
||||
|
||||
layout (binding = 1) uniform sampler2D sampler2d;
|
||||
|
||||
layout (location = 0) in vec2 inUV;
|
||||
layout (location = 1) in flat float lambda;
|
||||
|
||||
layout (location = 0) out vec4 outFragColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
outFragColor = textureLod(sampler2d, inUV, lambda);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// Copyright 2017 Mark Callow
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#version 450
|
||||
|
||||
#extension GL_ARB_separate_shader_objects : enable
|
||||
#extension GL_ARB_shading_language_420pack : enable
|
||||
|
||||
layout (location = 0) in vec4 inPos;
|
||||
layout (location = 1) in vec2 inUV;
|
||||
|
||||
struct Instance
|
||||
{
|
||||
mat4 model;
|
||||
};
|
||||
|
||||
// Keep the default value small to avoid MoltenVK issue 1420.
|
||||
// https://github.com/KhronosGroup/MoltenVK/issues/1420
|
||||
//layout(constant_id = 1) const int instanceCount = 1;
|
||||
// Sadly the above does not work on iOS, only macOS so declare
|
||||
// roughly the max size expected and in the app allocate the size
|
||||
// as declared.
|
||||
layout(constant_id = 1) const int instanceCount = 16;
|
||||
|
||||
layout (binding = 0, std140) uniform UBO
|
||||
{
|
||||
mat4 projection;
|
||||
mat4 view;
|
||||
Instance instance[instanceCount];
|
||||
} ubo;
|
||||
|
||||
layout (location = 0) out vec2 outUV;
|
||||
layout (location = 1) out flat float lambda;
|
||||
|
||||
void main()
|
||||
{
|
||||
outUV = inUV;
|
||||
lambda = gl_InstanceIndex+0.5;
|
||||
mat4 modelView = ubo.view * ubo.instance[gl_InstanceIndex].model;
|
||||
gl_Position = ubo.projection * modelView * inPos;
|
||||
}
|
||||
@@ -0,0 +1,673 @@
|
||||
/*
|
||||
* Simple wrapper for getting an index buffer and vertices out of an assimp mesh
|
||||
*
|
||||
* Copyright 2016 Sascha Willems - www.saschawillems.de
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Suppression of clang unused function warning added by Mark Callow, 2017.3.3.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <fcntl.h>
|
||||
#include <io.h>
|
||||
#else
|
||||
#endif
|
||||
|
||||
#include "vulkan/vulkan.h"
|
||||
|
||||
#include <assimp/Importer.hpp>
|
||||
#include <assimp/scene.h>
|
||||
#include <assimp/postprocess.h>
|
||||
#include <assimp/cimport.h>
|
||||
|
||||
#include "disable_glm_warnings.h"
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include "reenable_warnings.h"
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
#include <android/asset_manager.h>
|
||||
#endif
|
||||
|
||||
namespace vkMeshLoader
|
||||
{
|
||||
typedef enum VertexLayout {
|
||||
VERTEX_LAYOUT_POSITION = 0x0,
|
||||
VERTEX_LAYOUT_NORMAL = 0x1,
|
||||
VERTEX_LAYOUT_COLOR = 0x2,
|
||||
VERTEX_LAYOUT_UV = 0x3,
|
||||
VERTEX_LAYOUT_TANGENT = 0x4,
|
||||
VERTEX_LAYOUT_BITANGENT = 0x5,
|
||||
VERTEX_LAYOUT_DUMMY_FLOAT = 0x6,
|
||||
VERTEX_LAYOUT_DUMMY_VEC4 = 0x7
|
||||
} VertexLayout;
|
||||
|
||||
struct MeshBufferInfo
|
||||
{
|
||||
VkBuffer buf = VK_NULL_HANDLE;
|
||||
VkDeviceMemory mem = VK_NULL_HANDLE;
|
||||
size_t size = 0;
|
||||
};
|
||||
|
||||
struct MeshBuffer
|
||||
{
|
||||
MeshBufferInfo vertices;
|
||||
MeshBufferInfo indices;
|
||||
uint32_t indexCount;
|
||||
glm::vec3 dim;
|
||||
};
|
||||
|
||||
// Get vertex size from vertex layout
|
||||
static uint32_t vertexSize(std::vector<vkMeshLoader::VertexLayout> layout)
|
||||
{
|
||||
uint32_t vSize = 0;
|
||||
for (auto& layoutDetail : layout)
|
||||
{
|
||||
switch (layoutDetail)
|
||||
{
|
||||
// UV only has two components
|
||||
case VERTEX_LAYOUT_UV:
|
||||
vSize += 2 * sizeof(float);
|
||||
break;
|
||||
default:
|
||||
vSize += 3 * sizeof(float);
|
||||
}
|
||||
}
|
||||
return vSize;
|
||||
}
|
||||
|
||||
// Stores some additonal info and functions for
|
||||
// specifying pipelines, vertex bindings, etc.
|
||||
class Mesh
|
||||
{
|
||||
public:
|
||||
MeshBuffer buffers;
|
||||
|
||||
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
|
||||
VkPipeline pipeline = VK_NULL_HANDLE;
|
||||
VkDescriptorSet descriptorSet = VK_NULL_HANDLE;
|
||||
|
||||
uint32_t vertexBufferBinding = 0;
|
||||
|
||||
VkPipelineVertexInputStateCreateInfo vertexInputState;
|
||||
VkVertexInputBindingDescription bindingDescription;
|
||||
std::vector<VkVertexInputAttributeDescription> attributeDescriptions;
|
||||
|
||||
void setupVertexInputState(std::vector<vkMeshLoader::VertexLayout> layout)
|
||||
{
|
||||
bindingDescription = vkTools::initializers::vertexInputBindingDescription(
|
||||
vertexBufferBinding,
|
||||
vertexSize(layout),
|
||||
VK_VERTEX_INPUT_RATE_VERTEX);
|
||||
|
||||
attributeDescriptions.clear();
|
||||
uint32_t offset = 0;
|
||||
uint32_t binding = 0;
|
||||
for (auto& layoutDetail : layout)
|
||||
{
|
||||
// Format (layout)
|
||||
VkFormat format = (layoutDetail == VERTEX_LAYOUT_UV) ? VK_FORMAT_R32G32_SFLOAT : VK_FORMAT_R32G32B32_SFLOAT;
|
||||
|
||||
attributeDescriptions.push_back(
|
||||
vkTools::initializers::vertexInputAttributeDescription(
|
||||
vertexBufferBinding,
|
||||
binding,
|
||||
format,
|
||||
offset));
|
||||
|
||||
// Offset
|
||||
offset += (layoutDetail == VERTEX_LAYOUT_UV) ? (2 * sizeof(float)) : (3 * sizeof(float));
|
||||
binding++;
|
||||
}
|
||||
|
||||
vertexInputState = vkTools::initializers::pipelineVertexInputStateCreateInfo();
|
||||
vertexInputState.vertexBindingDescriptionCount = 1;
|
||||
vertexInputState.pVertexBindingDescriptions = &bindingDescription;
|
||||
vertexInputState.vertexAttributeDescriptionCount = static_cast<uint32_t>(attributeDescriptions.size());
|
||||
vertexInputState.pVertexAttributeDescriptions = attributeDescriptions.data();
|
||||
}
|
||||
|
||||
void drawIndexed(VkCommandBuffer cmdBuffer)
|
||||
{
|
||||
VkDeviceSize offsets[1] = { 0 };
|
||||
if (pipeline != VK_NULL_HANDLE)
|
||||
{
|
||||
vkCmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
||||
}
|
||||
if ((pipelineLayout != VK_NULL_HANDLE) && (descriptorSet != VK_NULL_HANDLE))
|
||||
{
|
||||
vkCmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, NULL);
|
||||
}
|
||||
vkCmdBindVertexBuffers(cmdBuffer, vertexBufferBinding, 1, &buffers.vertices.buf, offsets);
|
||||
vkCmdBindIndexBuffer(cmdBuffer, buffers.indices.buf, 0, VK_INDEX_TYPE_UINT32);
|
||||
vkCmdDrawIndexed(cmdBuffer, buffers.indexCount, 1, 0, 0, 0);
|
||||
}
|
||||
};
|
||||
|
||||
// Because this file is included in multiple .cpp files, via
|
||||
// VulkanLoadTestSample.h, and not all of those samples call this function...
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4505)
|
||||
#else
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-function"
|
||||
#endif
|
||||
static void freeMeshBufferResources(VkDevice device, vkMeshLoader::MeshBuffer *meshBuffer)
|
||||
{
|
||||
vkDestroyBuffer(device, meshBuffer->vertices.buf, nullptr);
|
||||
vkFreeMemory(device, meshBuffer->vertices.mem, nullptr);
|
||||
if (meshBuffer->indices.buf != VK_NULL_HANDLE)
|
||||
{
|
||||
vkDestroyBuffer(device, meshBuffer->indices.buf, nullptr);
|
||||
vkFreeMemory(device, meshBuffer->indices.mem, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(pop)
|
||||
#else
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
// Simple mesh class for getting all the necessary stuff from models loaded via ASSIMP
|
||||
class VulkanMeshLoader {
|
||||
private:
|
||||
|
||||
struct Vertex
|
||||
{
|
||||
glm::vec3 m_pos;
|
||||
glm::vec2 m_tex;
|
||||
glm::vec3 m_normal;
|
||||
glm::vec3 m_color;
|
||||
glm::vec3 m_tangent;
|
||||
glm::vec3 m_binormal;
|
||||
|
||||
Vertex() {}
|
||||
|
||||
Vertex(const glm::vec3& pos, const glm::vec2& tex, const glm::vec3& normal, const glm::vec3& tangent, const glm::vec3& bitangent, const glm::vec3& color)
|
||||
{
|
||||
m_pos = pos;
|
||||
m_tex = tex;
|
||||
m_normal = normal;
|
||||
m_color = color;
|
||||
m_tangent = tangent;
|
||||
m_binormal = bitangent;
|
||||
}
|
||||
};
|
||||
|
||||
struct MeshEntry {
|
||||
uint32_t NumIndices;
|
||||
uint32_t MaterialIndex;
|
||||
uint32_t vertexBase;
|
||||
std::vector<Vertex> Vertices;
|
||||
std::vector<unsigned int> Indices;
|
||||
};
|
||||
|
||||
VkBool32 getMemoryType(VkPhysicalDeviceMemoryProperties deviceMemoryProperties, uint32_t typeBits, VkMemoryPropertyFlags properties)
|
||||
{
|
||||
for (uint32_t i = 0; i < 32; i++)
|
||||
{
|
||||
if ((typeBits & 1) == 1)
|
||||
{
|
||||
if ((deviceMemoryProperties.memoryTypes[i].propertyFlags & properties) == properties)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
typeBits >>= 1;
|
||||
}
|
||||
|
||||
// todo : throw error
|
||||
return 0;
|
||||
}
|
||||
|
||||
public:
|
||||
#if defined(__ANDROID__)
|
||||
AAssetManager* assetManager = nullptr;
|
||||
#endif
|
||||
|
||||
std::vector<MeshEntry> m_Entries;
|
||||
|
||||
struct Dimension
|
||||
{
|
||||
glm::vec3 min = glm::vec3(FLT_MAX);
|
||||
glm::vec3 max = glm::vec3(-FLT_MAX);
|
||||
glm::vec3 size;
|
||||
} dim;
|
||||
|
||||
uint32_t numVertices = 0;
|
||||
|
||||
// Optional
|
||||
struct
|
||||
{
|
||||
VkBuffer buf;
|
||||
VkDeviceMemory mem;
|
||||
} deviceVertexBuffer;
|
||||
|
||||
struct {
|
||||
VkBuffer buf;
|
||||
VkDeviceMemory mem;
|
||||
uint32_t count;
|
||||
} deviceIndexBuffer;
|
||||
|
||||
VkPipelineVertexInputStateCreateInfo vi;
|
||||
std::vector<VkVertexInputBindingDescription> bindingDescriptions;
|
||||
std::vector<VkVertexInputAttributeDescription> attributeDescriptions;
|
||||
VkPipeline pipeline;
|
||||
|
||||
Assimp::Importer Importer;
|
||||
const aiScene* pScene;
|
||||
|
||||
~VulkanMeshLoader()
|
||||
{
|
||||
m_Entries.clear();
|
||||
}
|
||||
|
||||
// Loads the mesh with some default flags
|
||||
bool LoadMesh(const std::string& filename)
|
||||
{
|
||||
int flags = aiProcess_FlipWindingOrder | aiProcess_Triangulate | aiProcess_PreTransformVertices | aiProcess_CalcTangentSpace | aiProcess_GenSmoothNormals;
|
||||
|
||||
return LoadMesh(filename, flags);
|
||||
}
|
||||
|
||||
// Load the mesh with custom flags
|
||||
bool LoadMesh(const std::string& filename, int flags)
|
||||
{
|
||||
#if defined(__ANDROID__)
|
||||
// Meshes are stored inside the apk on Android (compressed)
|
||||
// So they need to be loaded via the asset manager
|
||||
|
||||
AAsset* asset = AAssetManager_open(assetManager, filename.c_str(), AASSET_MODE_STREAMING);
|
||||
assert(asset);
|
||||
size_t size = AAsset_getLength(asset);
|
||||
|
||||
assert(size > 0);
|
||||
|
||||
void *meshData = malloc(size);
|
||||
AAsset_read(asset, meshData, size);
|
||||
AAsset_close(asset);
|
||||
|
||||
pScene = Importer.ReadFileFromMemory(meshData, size, flags);
|
||||
|
||||
free(meshData);
|
||||
#else
|
||||
pScene = Importer.ReadFile(filename.c_str(), flags);
|
||||
#endif
|
||||
|
||||
if (pScene)
|
||||
{
|
||||
return InitFromScene(pScene, filename);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Error parsing '%s': '%s'\n", filename.c_str(), Importer.GetErrorString());
|
||||
#if defined(__ANDROID__)
|
||||
LOGE("Error parsing '%s': '%s'", filename.c_str(), Importer.GetErrorString());
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool InitFromScene(const aiScene* pSrcScene, const std::string& /*Filename*/)
|
||||
{
|
||||
m_Entries.resize(pSrcScene->mNumMeshes);
|
||||
|
||||
// Counters
|
||||
for (unsigned int i = 0; i < m_Entries.size(); i++)
|
||||
{
|
||||
m_Entries[i].vertexBase = numVertices;
|
||||
numVertices += pSrcScene->mMeshes[i]->mNumVertices;
|
||||
}
|
||||
|
||||
// Initialize the meshes in the scene one by one
|
||||
for (unsigned int i = 0; i < m_Entries.size(); i++)
|
||||
{
|
||||
const aiMesh* paiMesh = pSrcScene->mMeshes[i];
|
||||
InitMesh(i, paiMesh, pSrcScene);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void InitMesh(unsigned int index, const aiMesh* paiMesh, const aiScene* pSrcScene)
|
||||
{
|
||||
m_Entries[index].MaterialIndex = paiMesh->mMaterialIndex;
|
||||
|
||||
aiColor3D pColor(0.f, 0.f, 0.f);
|
||||
pSrcScene->mMaterials[paiMesh->mMaterialIndex]->Get(AI_MATKEY_COLOR_DIFFUSE, pColor);
|
||||
|
||||
aiVector3D Zero3D(0.0f, 0.0f, 0.0f);
|
||||
|
||||
for (unsigned int i = 0; i < paiMesh->mNumVertices; i++) {
|
||||
aiVector3D* pPos = &(paiMesh->mVertices[i]);
|
||||
aiVector3D* pNormal = &(paiMesh->mNormals[i]);
|
||||
aiVector3D *pTexCoord;
|
||||
if (paiMesh->HasTextureCoords(0))
|
||||
{
|
||||
pTexCoord = &(paiMesh->mTextureCoords[0][i]);
|
||||
}
|
||||
else {
|
||||
pTexCoord = &Zero3D;
|
||||
}
|
||||
aiVector3D* pTangent = (paiMesh->HasTangentsAndBitangents()) ? &(paiMesh->mTangents[i]) : &Zero3D;
|
||||
aiVector3D* pBiTangent = (paiMesh->HasTangentsAndBitangents()) ? &(paiMesh->mBitangents[i]) : &Zero3D;
|
||||
|
||||
Vertex v(glm::vec3(pPos->x, -pPos->y, pPos->z),
|
||||
glm::vec2(pTexCoord->x , pTexCoord->y),
|
||||
glm::vec3(pNormal->x, pNormal->y, pNormal->z),
|
||||
glm::vec3(pTangent->x, pTangent->y, pTangent->z),
|
||||
glm::vec3(pBiTangent->x, pBiTangent->y, pBiTangent->z),
|
||||
glm::vec3(pColor.r, pColor.g, pColor.b)
|
||||
);
|
||||
|
||||
dim.max.x = fmax(pPos->x, dim.max.x);
|
||||
dim.max.y = fmax(pPos->y, dim.max.y);
|
||||
dim.max.z = fmax(pPos->z, dim.max.z);
|
||||
|
||||
dim.min.x = fmin(pPos->x, dim.min.x);
|
||||
dim.min.y = fmin(pPos->y, dim.min.y);
|
||||
dim.min.z = fmin(pPos->z, dim.min.z);
|
||||
|
||||
m_Entries[index].Vertices.push_back(v);
|
||||
}
|
||||
|
||||
dim.size = dim.max - dim.min;
|
||||
|
||||
for (unsigned int i = 0; i < paiMesh->mNumFaces; i++)
|
||||
{
|
||||
const aiFace& Face = paiMesh->mFaces[i];
|
||||
if (Face.mNumIndices != 3)
|
||||
continue;
|
||||
m_Entries[index].Indices.push_back(Face.mIndices[0]);
|
||||
m_Entries[index].Indices.push_back(Face.mIndices[1]);
|
||||
m_Entries[index].Indices.push_back(Face.mIndices[2]);
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up vulkan resources used by a mesh
|
||||
static void freeVulkanResources(VkDevice device, VulkanMeshLoader *mesh)
|
||||
{
|
||||
vkDestroyBuffer(device, mesh->deviceVertexBuffer.buf, nullptr);
|
||||
vkFreeMemory(device, mesh->deviceVertexBuffer.mem, nullptr);
|
||||
vkDestroyBuffer(device, mesh->deviceIndexBuffer.buf, nullptr);
|
||||
vkFreeMemory(device, mesh->deviceIndexBuffer.mem, nullptr);
|
||||
}
|
||||
|
||||
VkResult createBuffer(
|
||||
VkDevice device,
|
||||
VkPhysicalDeviceMemoryProperties deviceMemoryProperties,
|
||||
VkBufferUsageFlags usageFlags,
|
||||
VkMemoryPropertyFlags memoryPropertyFlags,
|
||||
VkDeviceSize size,
|
||||
VkBuffer *buffer,
|
||||
VkDeviceMemory *memory)
|
||||
{
|
||||
VkMemoryAllocateInfo memAllocInfo = vkTools::initializers::memoryAllocateInfo();
|
||||
VkMemoryRequirements memReqs;
|
||||
|
||||
VkBufferCreateInfo bufferInfo = vkTools::initializers::bufferCreateInfo(usageFlags, size);
|
||||
VK_CHECK_RESULT(vkCreateBuffer(device, &bufferInfo, nullptr, buffer));
|
||||
vkGetBufferMemoryRequirements(device, *buffer, &memReqs);
|
||||
memAllocInfo.allocationSize = memReqs.size;
|
||||
memAllocInfo.memoryTypeIndex = getMemoryType(deviceMemoryProperties, memReqs.memoryTypeBits, memoryPropertyFlags);
|
||||
VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, memory));
|
||||
VK_CHECK_RESULT(vkBindBufferMemory(device, *buffer, *memory, 0));
|
||||
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
// Create vertex and index buffer with given layout
|
||||
// Note : Only does staging if a valid command buffer and transfer queue are passed
|
||||
void createBuffers(
|
||||
VkDevice device,
|
||||
VkPhysicalDeviceMemoryProperties deviceMemoryProperties,
|
||||
vkMeshLoader::MeshBuffer *meshBuffer,
|
||||
std::vector<vkMeshLoader::VertexLayout> layout,
|
||||
float scale,
|
||||
bool useStaging,
|
||||
VkCommandBuffer copyCmd,
|
||||
VkQueue copyQueue)
|
||||
{
|
||||
std::vector<float> vertexBuffer;
|
||||
for (uint32_t m = 0; m < m_Entries.size(); m++)
|
||||
{
|
||||
for (uint32_t i = 0; i < m_Entries[m].Vertices.size(); i++)
|
||||
{
|
||||
// Push vertex data depending on layout
|
||||
for (auto& layoutDetail : layout)
|
||||
{
|
||||
// Position
|
||||
if (layoutDetail == vkMeshLoader::VERTEX_LAYOUT_POSITION)
|
||||
{
|
||||
vertexBuffer.push_back(m_Entries[m].Vertices[i].m_pos.x * scale);
|
||||
vertexBuffer.push_back(m_Entries[m].Vertices[i].m_pos.y * scale);
|
||||
vertexBuffer.push_back(m_Entries[m].Vertices[i].m_pos.z * scale);
|
||||
}
|
||||
// Normal
|
||||
if (layoutDetail == vkMeshLoader::VERTEX_LAYOUT_NORMAL)
|
||||
{
|
||||
vertexBuffer.push_back(m_Entries[m].Vertices[i].m_normal.x);
|
||||
vertexBuffer.push_back(-m_Entries[m].Vertices[i].m_normal.y);
|
||||
vertexBuffer.push_back(m_Entries[m].Vertices[i].m_normal.z);
|
||||
}
|
||||
// Texture coordinates
|
||||
if (layoutDetail == vkMeshLoader::VERTEX_LAYOUT_UV)
|
||||
{
|
||||
vertexBuffer.push_back(m_Entries[m].Vertices[i].m_tex.s);
|
||||
vertexBuffer.push_back(m_Entries[m].Vertices[i].m_tex.t);
|
||||
}
|
||||
// Color
|
||||
if (layoutDetail == vkMeshLoader::VERTEX_LAYOUT_COLOR)
|
||||
{
|
||||
vertexBuffer.push_back(m_Entries[m].Vertices[i].m_color.r);
|
||||
vertexBuffer.push_back(m_Entries[m].Vertices[i].m_color.g);
|
||||
vertexBuffer.push_back(m_Entries[m].Vertices[i].m_color.b);
|
||||
}
|
||||
// Tangent
|
||||
if (layoutDetail == vkMeshLoader::VERTEX_LAYOUT_TANGENT)
|
||||
{
|
||||
vertexBuffer.push_back(m_Entries[m].Vertices[i].m_tangent.x);
|
||||
vertexBuffer.push_back(m_Entries[m].Vertices[i].m_tangent.y);
|
||||
vertexBuffer.push_back(m_Entries[m].Vertices[i].m_tangent.z);
|
||||
}
|
||||
// Bitangent
|
||||
if (layoutDetail == vkMeshLoader::VERTEX_LAYOUT_BITANGENT)
|
||||
{
|
||||
vertexBuffer.push_back(m_Entries[m].Vertices[i].m_binormal.x);
|
||||
vertexBuffer.push_back(m_Entries[m].Vertices[i].m_binormal.y);
|
||||
vertexBuffer.push_back(m_Entries[m].Vertices[i].m_binormal.z);
|
||||
}
|
||||
// Dummy layout components for padding
|
||||
if (layoutDetail == vkMeshLoader::VERTEX_LAYOUT_DUMMY_FLOAT)
|
||||
{
|
||||
vertexBuffer.push_back(0.0f);
|
||||
}
|
||||
if (layoutDetail == vkMeshLoader::VERTEX_LAYOUT_DUMMY_VEC4)
|
||||
{
|
||||
vertexBuffer.push_back(0.0f);
|
||||
vertexBuffer.push_back(0.0f);
|
||||
vertexBuffer.push_back(0.0f);
|
||||
vertexBuffer.push_back(0.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
meshBuffer->vertices.size = vertexBuffer.size() * sizeof(float);
|
||||
|
||||
dim.min *= scale;
|
||||
dim.max *= scale;
|
||||
dim.size *= scale;
|
||||
|
||||
std::vector<uint32_t> indexBuffer;
|
||||
for (uint32_t m = 0; m < m_Entries.size(); m++)
|
||||
{
|
||||
uint32_t indexBase = (uint32_t)indexBuffer.size();
|
||||
for (uint32_t i = 0; i < m_Entries[m].Indices.size(); i++)
|
||||
{
|
||||
indexBuffer.push_back(m_Entries[m].Indices[i] + indexBase);
|
||||
}
|
||||
}
|
||||
meshBuffer->indices.size = indexBuffer.size() * sizeof(uint32_t);
|
||||
|
||||
meshBuffer->indexCount = (uint32_t)indexBuffer.size();
|
||||
|
||||
void* data;
|
||||
|
||||
// Use staging buffer to move vertex and index buffer to device local memory
|
||||
if (useStaging && copyQueue != VK_NULL_HANDLE && copyCmd != VK_NULL_HANDLE)
|
||||
{
|
||||
// Create staging buffers
|
||||
struct {
|
||||
VkBuffer buffer;
|
||||
VkDeviceMemory memory;
|
||||
} vertexStaging, indexStaging;
|
||||
|
||||
// Vertex buffer
|
||||
createBuffer(
|
||||
device,
|
||||
deviceMemoryProperties,
|
||||
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
|
||||
meshBuffer->vertices.size,
|
||||
&vertexStaging.buffer,
|
||||
&vertexStaging.memory);
|
||||
|
||||
VK_CHECK_RESULT(vkMapMemory(device, vertexStaging.memory, 0, VK_WHOLE_SIZE, 0, &data));
|
||||
memcpy(data, vertexBuffer.data(), meshBuffer->vertices.size);
|
||||
vkUnmapMemory(device, vertexStaging.memory);
|
||||
|
||||
// Index buffer
|
||||
createBuffer(
|
||||
device,
|
||||
deviceMemoryProperties,
|
||||
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
|
||||
meshBuffer->indices.size,
|
||||
&indexStaging.buffer,
|
||||
&indexStaging.memory);
|
||||
|
||||
VK_CHECK_RESULT(vkMapMemory(device, indexStaging.memory, 0, VK_WHOLE_SIZE, 0, &data));
|
||||
memcpy(data, indexBuffer.data(), meshBuffer->indices.size);
|
||||
vkUnmapMemory(device, indexStaging.memory);
|
||||
|
||||
// Create device local target buffers
|
||||
// Vertex buffer
|
||||
createBuffer(
|
||||
device,
|
||||
deviceMemoryProperties,
|
||||
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
|
||||
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
|
||||
meshBuffer->vertices.size,
|
||||
&meshBuffer->vertices.buf,
|
||||
&meshBuffer->vertices.mem);
|
||||
|
||||
// Index buffer
|
||||
createBuffer(
|
||||
device,
|
||||
deviceMemoryProperties,
|
||||
VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
|
||||
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
|
||||
meshBuffer->indices.size,
|
||||
&meshBuffer->indices.buf,
|
||||
&meshBuffer->indices.mem);
|
||||
|
||||
// Copy from staging buffers
|
||||
VkCommandBufferBeginInfo cmdBufInfo = vkTools::initializers::commandBufferBeginInfo();
|
||||
VK_CHECK_RESULT(vkBeginCommandBuffer(copyCmd, &cmdBufInfo));
|
||||
|
||||
VkBufferCopy copyRegion = {};
|
||||
|
||||
copyRegion.size = meshBuffer->vertices.size;
|
||||
vkCmdCopyBuffer(
|
||||
copyCmd,
|
||||
vertexStaging.buffer,
|
||||
meshBuffer->vertices.buf,
|
||||
1,
|
||||
©Region);
|
||||
|
||||
copyRegion.size = meshBuffer->indices.size;
|
||||
vkCmdCopyBuffer(
|
||||
copyCmd,
|
||||
indexStaging.buffer,
|
||||
meshBuffer->indices.buf,
|
||||
1,
|
||||
©Region);
|
||||
|
||||
VK_CHECK_RESULT(vkEndCommandBuffer(copyCmd));
|
||||
|
||||
VkSubmitInfo submitInfo = {};
|
||||
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
||||
submitInfo.commandBufferCount = 1;
|
||||
submitInfo.pCommandBuffers = ©Cmd;
|
||||
|
||||
VK_CHECK_RESULT(vkQueueSubmit(copyQueue, 1, &submitInfo, VK_NULL_HANDLE));
|
||||
VK_CHECK_RESULT(vkQueueWaitIdle(copyQueue));
|
||||
|
||||
vkDestroyBuffer(device, vertexStaging.buffer, nullptr);
|
||||
vkFreeMemory(device, vertexStaging.memory, nullptr);
|
||||
vkDestroyBuffer(device, indexStaging.buffer, nullptr);
|
||||
vkFreeMemory(device, indexStaging.memory, nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Generate vertex buffer
|
||||
createBuffer(
|
||||
device,
|
||||
deviceMemoryProperties,
|
||||
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
|
||||
meshBuffer->vertices.size,
|
||||
&meshBuffer->vertices.buf,
|
||||
&meshBuffer->vertices.mem);
|
||||
|
||||
VK_CHECK_RESULT(vkMapMemory(device, meshBuffer->vertices.mem, 0, meshBuffer->vertices.size, 0, &data));
|
||||
memcpy(data, vertexBuffer.data(), meshBuffer->vertices.size);
|
||||
vkUnmapMemory(device, meshBuffer->vertices.mem);
|
||||
|
||||
// Generate index buffer
|
||||
createBuffer(
|
||||
device,
|
||||
deviceMemoryProperties,
|
||||
VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
|
||||
meshBuffer->indices.size,
|
||||
&meshBuffer->indices.buf,
|
||||
&meshBuffer->indices.mem);
|
||||
|
||||
VK_CHECK_RESULT(vkMapMemory(device, meshBuffer->indices.mem, 0, meshBuffer->indices.size, 0, &data));
|
||||
memcpy(data, indexBuffer.data(), meshBuffer->indices.size);
|
||||
vkUnmapMemory(device, meshBuffer->indices.mem);
|
||||
}
|
||||
}
|
||||
|
||||
// Create vertex and index buffer with given layout
|
||||
void createVulkanBuffers(
|
||||
VkDevice device,
|
||||
VkPhysicalDeviceMemoryProperties deviceMemoryProperties,
|
||||
vkMeshLoader::MeshBuffer *meshBuffer,
|
||||
std::vector<vkMeshLoader::VertexLayout> layout,
|
||||
float scale)
|
||||
{
|
||||
createBuffers(
|
||||
device,
|
||||
deviceMemoryProperties,
|
||||
meshBuffer,
|
||||
layout,
|
||||
scale,
|
||||
false,
|
||||
VK_NULL_HANDLE,
|
||||
VK_NULL_HANDLE);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,66 @@
|
||||
/* -*- tab-width: 4; -*- */
|
||||
/* vi: set sw=2 ts=4 expandtab: */
|
||||
|
||||
/*
|
||||
* Copyright 2017-2020 Mark Callow.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ktx.h>
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
#include "VulkanContext.h"
|
||||
|
||||
class TextureTranscoder {
|
||||
public:
|
||||
TextureTranscoder(VulkanContext& vkctx) {
|
||||
vkctx.gpu.getFeatures(&deviceFeatures);
|
||||
|
||||
if (deviceFeatures.textureCompressionASTC_LDR)
|
||||
defaultTf = KTX_TTF_ASTC_4x4_RGBA;
|
||||
else if (deviceFeatures.textureCompressionETC2)
|
||||
defaultTf = KTX_TTF_ETC;
|
||||
else if (deviceFeatures.textureCompressionBC)
|
||||
defaultTf = KTX_TTF_BC1_OR_3;
|
||||
else if (vkctx.enabledDeviceExtensions.pvrtc) {
|
||||
defaultTf = KTX_TTF_PVRTC2_4_RGBA;
|
||||
} else {
|
||||
std::stringstream message;
|
||||
|
||||
message << "Vulkan implementation does not support any available transcode target.";
|
||||
throw std::runtime_error(message.str());
|
||||
}
|
||||
}
|
||||
|
||||
void transcode(ktxTexture2* kTexture) {
|
||||
KTX_error_code ktxresult;
|
||||
ktx_transcode_fmt_e tf;
|
||||
khr_df_model_e colorModel = ktxTexture2_GetColorModel_e(kTexture);
|
||||
|
||||
if (colorModel == KHR_DF_MODEL_UASTC
|
||||
&& deviceFeatures.textureCompressionASTC_LDR) {
|
||||
tf = KTX_TTF_ASTC_4x4_RGBA;
|
||||
} else if (colorModel == KHR_DF_MODEL_ETC1S
|
||||
&& deviceFeatures.textureCompressionETC2) {
|
||||
tf = KTX_TTF_ETC;
|
||||
} else {
|
||||
tf = defaultTf;
|
||||
}
|
||||
|
||||
ktxresult = ktxTexture2_TranscodeBasis(kTexture, tf, 0);
|
||||
if (KTX_SUCCESS != ktxresult) {
|
||||
std::stringstream message;
|
||||
|
||||
message << "Transcoding of ktxTexture2 to "
|
||||
<< ktxTranscodeFormatString(tf) << " failed: "
|
||||
<< ktxErrorString(ktxresult);
|
||||
throw std::runtime_error(message.str());
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
ktx_transcode_fmt_e defaultTf;
|
||||
vk::PhysicalDeviceFeatures deviceFeatures;
|
||||
};
|
||||
Reference in New Issue
Block a user