Merge branch 'variable_rate_shading'
This commit is contained in:
commit
3cb3df4cbf
15 changed files with 953 additions and 28 deletions
|
|
@ -382,6 +382,11 @@ An updated version using ```VK_EXT_debug_utils``` along with an in-depth tutoria
|
||||||
|
|
||||||
Shows how to render a scene using a negative viewport height, making the Vulkan render setup more similar to other APIs like OpenGL. Also has several options for changing relevant pipeline state, and displaying meshes with OpenGL or Vulkan style coordinates. Details can be found in [this tutorial](https://www.saschawillems.de/tutorials/vulkan/flipping-viewport).
|
Shows how to render a scene using a negative viewport height, making the Vulkan render setup more similar to other APIs like OpenGL. Also has several options for changing relevant pipeline state, and displaying meshes with OpenGL or Vulkan style coordinates. Details can be found in [this tutorial](https://www.saschawillems.de/tutorials/vulkan/flipping-viewport).
|
||||||
|
|
||||||
|
#### [08 - Variable rate shading (VK_NV_shading_rate_image)](examples/variablerateshading/)
|
||||||
|
|
||||||
|
Uses a special image that contains variable shading rates to vary the number of fragment shader invocations across the framebuffer. This makes it possible to lower fragment shader invocations for less important/less noisy parts of the framebuffer.
|
||||||
|
|
||||||
|
|
||||||
### <a name="Misc"></a> Misc
|
### <a name="Misc"></a> Misc
|
||||||
|
|
||||||
#### [01 - Vulkan Gears](examples/gears/)
|
#### [01 - Vulkan Gears](examples/gears/)
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
VkDescriptorSetLayout vkglTF::descriptorSetLayoutImage = VK_NULL_HANDLE;
|
VkDescriptorSetLayout vkglTF::descriptorSetLayoutImage = VK_NULL_HANDLE;
|
||||||
VkDescriptorSetLayout vkglTF::descriptorSetLayoutUbo = VK_NULL_HANDLE;
|
VkDescriptorSetLayout vkglTF::descriptorSetLayoutUbo = VK_NULL_HANDLE;
|
||||||
VkMemoryPropertyFlags vkglTF::memoryPropertyFlags = 0;
|
VkMemoryPropertyFlags vkglTF::memoryPropertyFlags = 0;
|
||||||
|
uint32_t vkglTF::descriptorBindingFlags = vkglTF::DescriptorBindingFlags::ImageBaseColor;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
We use a custom image loading function with tinyglTF, so we can do custom stuff loading ktx textures
|
We use a custom image loading function with tinyglTF, so we can do custom stuff loading ktx textures
|
||||||
|
|
@ -428,7 +429,7 @@ void vkglTF::Texture::fromglTfImage(tinygltf::Image &gltfimage, std::string path
|
||||||
/*
|
/*
|
||||||
glTF material
|
glTF material
|
||||||
*/
|
*/
|
||||||
void vkglTF::Material::createDescriptorSet(VkDescriptorPool descriptorPool, VkDescriptorSetLayout descriptorSetLayout)
|
void vkglTF::Material::createDescriptorSet(VkDescriptorPool descriptorPool, VkDescriptorSetLayout descriptorSetLayout, uint32_t descriptorBindingFlags)
|
||||||
{
|
{
|
||||||
VkDescriptorSetAllocateInfo descriptorSetAllocInfo{};
|
VkDescriptorSetAllocateInfo descriptorSetAllocInfo{};
|
||||||
descriptorSetAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
descriptorSetAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
||||||
|
|
@ -436,19 +437,31 @@ void vkglTF::Material::createDescriptorSet(VkDescriptorPool descriptorPool, VkDe
|
||||||
descriptorSetAllocInfo.pSetLayouts = &descriptorSetLayout;
|
descriptorSetAllocInfo.pSetLayouts = &descriptorSetLayout;
|
||||||
descriptorSetAllocInfo.descriptorSetCount = 1;
|
descriptorSetAllocInfo.descriptorSetCount = 1;
|
||||||
VK_CHECK_RESULT(vkAllocateDescriptorSets(device->logicalDevice, &descriptorSetAllocInfo, &descriptorSet));
|
VK_CHECK_RESULT(vkAllocateDescriptorSets(device->logicalDevice, &descriptorSetAllocInfo, &descriptorSet));
|
||||||
|
std::vector<VkDescriptorImageInfo> imageDescriptors{};
|
||||||
std::vector<VkDescriptorImageInfo> imageDescriptors = {
|
std::vector<VkWriteDescriptorSet> writeDescriptorSets{};
|
||||||
baseColorTexture->descriptor,
|
if (descriptorBindingFlags & DescriptorBindingFlags::ImageBaseColor) {
|
||||||
};
|
imageDescriptors.push_back(baseColorTexture->descriptor);
|
||||||
|
VkWriteDescriptorSet writeDescriptorSet{};
|
||||||
VkWriteDescriptorSet writeDescriptorSet{};
|
writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||||
writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
writeDescriptorSet.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||||
writeDescriptorSet.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
writeDescriptorSet.descriptorCount = 1;
|
||||||
writeDescriptorSet.descriptorCount = 1;
|
writeDescriptorSet.dstSet = descriptorSet;
|
||||||
writeDescriptorSet.dstSet = descriptorSet;
|
writeDescriptorSet.dstBinding = static_cast<uint32_t>(writeDescriptorSets.size());
|
||||||
writeDescriptorSet.dstBinding = 0;
|
writeDescriptorSet.pImageInfo = &baseColorTexture->descriptor;
|
||||||
writeDescriptorSet.pImageInfo = &baseColorTexture->descriptor;
|
writeDescriptorSets.push_back(writeDescriptorSet);
|
||||||
vkUpdateDescriptorSets(device->logicalDevice, 1, &writeDescriptorSet, 0, nullptr);
|
}
|
||||||
|
if (normalTexture && descriptorBindingFlags & DescriptorBindingFlags::ImageNormalMap) {
|
||||||
|
imageDescriptors.push_back(normalTexture->descriptor);
|
||||||
|
VkWriteDescriptorSet writeDescriptorSet{};
|
||||||
|
writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||||
|
writeDescriptorSet.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||||
|
writeDescriptorSet.descriptorCount = 1;
|
||||||
|
writeDescriptorSet.dstSet = descriptorSet;
|
||||||
|
writeDescriptorSet.dstBinding = static_cast<uint32_t>(writeDescriptorSets.size());
|
||||||
|
writeDescriptorSet.pImageInfo = &normalTexture->descriptor;
|
||||||
|
writeDescriptorSets.push_back(writeDescriptorSet);
|
||||||
|
}
|
||||||
|
vkUpdateDescriptorSets(device->logicalDevice, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -600,6 +613,110 @@ vkglTF::Texture* vkglTF::Model::getTexture(uint32_t index)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void vkglTF::Model::createEmptyTexture(VkQueue transferQueue)
|
||||||
|
{
|
||||||
|
emptyTexture.device = device;
|
||||||
|
emptyTexture.width = 1;
|
||||||
|
emptyTexture.height = 1;
|
||||||
|
emptyTexture.layerCount = 1;
|
||||||
|
emptyTexture.mipLevels = 1;
|
||||||
|
|
||||||
|
size_t bufferSize = emptyTexture.width * emptyTexture.height * 4;
|
||||||
|
unsigned char* buffer = new unsigned char[bufferSize];
|
||||||
|
memset(buffer, 0, bufferSize);
|
||||||
|
|
||||||
|
VkBuffer stagingBuffer;
|
||||||
|
VkDeviceMemory stagingMemory;
|
||||||
|
VkBufferCreateInfo bufferCreateInfo = vks::initializers::bufferCreateInfo();
|
||||||
|
bufferCreateInfo.size = bufferSize;
|
||||||
|
// This buffer is used as a transfer source for the buffer copy
|
||||||
|
bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
|
||||||
|
bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||||
|
VK_CHECK_RESULT(vkCreateBuffer(device->logicalDevice, &bufferCreateInfo, nullptr, &stagingBuffer));
|
||||||
|
|
||||||
|
VkMemoryAllocateInfo memAllocInfo = vks::initializers::memoryAllocateInfo();
|
||||||
|
VkMemoryRequirements memReqs;
|
||||||
|
vkGetBufferMemoryRequirements(device->logicalDevice, stagingBuffer, &memReqs);
|
||||||
|
memAllocInfo.allocationSize = memReqs.size;
|
||||||
|
memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
|
||||||
|
VK_CHECK_RESULT(vkAllocateMemory(device->logicalDevice, &memAllocInfo, nullptr, &stagingMemory));
|
||||||
|
VK_CHECK_RESULT(vkBindBufferMemory(device->logicalDevice, stagingBuffer, stagingMemory, 0));
|
||||||
|
|
||||||
|
// Copy texture data into staging buffer
|
||||||
|
uint8_t* data;
|
||||||
|
VK_CHECK_RESULT(vkMapMemory(device->logicalDevice, stagingMemory, 0, memReqs.size, 0, (void**)&data));
|
||||||
|
memcpy(data, buffer, bufferSize);
|
||||||
|
vkUnmapMemory(device->logicalDevice, stagingMemory);
|
||||||
|
|
||||||
|
VkBufferImageCopy bufferCopyRegion = {};
|
||||||
|
bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
|
bufferCopyRegion.imageSubresource.layerCount = 1;
|
||||||
|
bufferCopyRegion.imageExtent.width = emptyTexture.width;
|
||||||
|
bufferCopyRegion.imageExtent.height = emptyTexture.height;
|
||||||
|
bufferCopyRegion.imageExtent.depth = 1;
|
||||||
|
|
||||||
|
// Create optimal tiled target image
|
||||||
|
VkImageCreateInfo imageCreateInfo = vks::initializers::imageCreateInfo();
|
||||||
|
imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
|
||||||
|
imageCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
|
||||||
|
imageCreateInfo.mipLevels = 1;
|
||||||
|
imageCreateInfo.arrayLayers = 1;
|
||||||
|
imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||||
|
imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||||
|
imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||||
|
imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||||
|
imageCreateInfo.extent = { emptyTexture.width, emptyTexture.height, 1 };
|
||||||
|
imageCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||||
|
VK_CHECK_RESULT(vkCreateImage(device->logicalDevice, &imageCreateInfo, nullptr, &emptyTexture.image));
|
||||||
|
|
||||||
|
vkGetImageMemoryRequirements(device->logicalDevice, emptyTexture.image, &memReqs);
|
||||||
|
memAllocInfo.allocationSize = memReqs.size;
|
||||||
|
memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||||
|
VK_CHECK_RESULT(vkAllocateMemory(device->logicalDevice, &memAllocInfo, nullptr, &emptyTexture.deviceMemory));
|
||||||
|
VK_CHECK_RESULT(vkBindImageMemory(device->logicalDevice, emptyTexture.image, emptyTexture.deviceMemory, 0));
|
||||||
|
|
||||||
|
VkImageSubresourceRange subresourceRange{};
|
||||||
|
subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
|
subresourceRange.baseMipLevel = 0;
|
||||||
|
subresourceRange.levelCount = 1;
|
||||||
|
subresourceRange.layerCount = 1;
|
||||||
|
|
||||||
|
VkCommandBuffer copyCmd = device->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
|
||||||
|
vks::tools::setImageLayout(copyCmd, emptyTexture.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, subresourceRange);
|
||||||
|
vkCmdCopyBufferToImage(copyCmd, stagingBuffer, emptyTexture.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &bufferCopyRegion);
|
||||||
|
vks::tools::setImageLayout(copyCmd, emptyTexture.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, subresourceRange);
|
||||||
|
device->flushCommandBuffer(copyCmd, transferQueue);
|
||||||
|
emptyTexture.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||||
|
|
||||||
|
// Clean up staging resources
|
||||||
|
vkFreeMemory(device->logicalDevice, stagingMemory, nullptr);
|
||||||
|
vkDestroyBuffer(device->logicalDevice, stagingBuffer, nullptr);
|
||||||
|
|
||||||
|
VkSamplerCreateInfo samplerCreateInfo = vks::initializers::samplerCreateInfo();
|
||||||
|
samplerCreateInfo.magFilter = VK_FILTER_LINEAR;
|
||||||
|
samplerCreateInfo.minFilter = VK_FILTER_LINEAR;
|
||||||
|
samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
|
||||||
|
samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
|
||||||
|
samplerCreateInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
|
||||||
|
samplerCreateInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
|
||||||
|
samplerCreateInfo.compareOp = VK_COMPARE_OP_NEVER;
|
||||||
|
samplerCreateInfo.maxAnisotropy = 1.0f;
|
||||||
|
VK_CHECK_RESULT(vkCreateSampler(device->logicalDevice, &samplerCreateInfo, nullptr, &emptyTexture.sampler));
|
||||||
|
|
||||||
|
VkImageViewCreateInfo viewCreateInfo = vks::initializers::imageViewCreateInfo();
|
||||||
|
viewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||||
|
viewCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
|
||||||
|
viewCreateInfo.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A };
|
||||||
|
viewCreateInfo.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
|
||||||
|
viewCreateInfo.subresourceRange.levelCount = 1;
|
||||||
|
viewCreateInfo.image = emptyTexture.image;
|
||||||
|
VK_CHECK_RESULT(vkCreateImageView(device->logicalDevice, &viewCreateInfo, nullptr, &emptyTexture.view));
|
||||||
|
|
||||||
|
emptyTexture.descriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||||
|
emptyTexture.descriptor.imageView = emptyTexture.view;
|
||||||
|
emptyTexture.descriptor.sampler = emptyTexture.sampler;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
glTF model loading and rendering class
|
glTF model loading and rendering class
|
||||||
*/
|
*/
|
||||||
|
|
@ -624,6 +741,7 @@ vkglTF::Model::~Model()
|
||||||
descriptorSetLayoutImage = VK_NULL_HANDLE;
|
descriptorSetLayoutImage = VK_NULL_HANDLE;
|
||||||
}
|
}
|
||||||
vkDestroyDescriptorPool(device->logicalDevice, descriptorPool, nullptr);
|
vkDestroyDescriptorPool(device->logicalDevice, descriptorPool, nullptr);
|
||||||
|
emptyTexture.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
void vkglTF::Model::loadNode(vkglTF::Node *parent, const tinygltf::Node &node, uint32_t nodeIndex, const tinygltf::Model &model, std::vector<uint32_t>& indexBuffer, std::vector<Vertex>& vertexBuffer, float globalscale)
|
void vkglTF::Model::loadNode(vkglTF::Node *parent, const tinygltf::Node &node, uint32_t nodeIndex, const tinygltf::Model &model, std::vector<uint32_t>& indexBuffer, std::vector<Vertex>& vertexBuffer, float globalscale)
|
||||||
|
|
@ -863,6 +981,8 @@ void vkglTF::Model::loadImages(tinygltf::Model &gltfModel, vks::VulkanDevice *de
|
||||||
texture.fromglTfImage(image, path, device, transferQueue);
|
texture.fromglTfImage(image, path, device, transferQueue);
|
||||||
textures.push_back(texture);
|
textures.push_back(texture);
|
||||||
}
|
}
|
||||||
|
// Create an empty texture to be used for empty material images
|
||||||
|
createEmptyTexture(transferQueue);
|
||||||
}
|
}
|
||||||
|
|
||||||
void vkglTF::Model::loadMaterials(tinygltf::Model &gltfModel)
|
void vkglTF::Model::loadMaterials(tinygltf::Model &gltfModel)
|
||||||
|
|
@ -887,6 +1007,8 @@ void vkglTF::Model::loadMaterials(tinygltf::Model &gltfModel)
|
||||||
}
|
}
|
||||||
if (mat.additionalValues.find("normalTexture") != mat.additionalValues.end()) {
|
if (mat.additionalValues.find("normalTexture") != mat.additionalValues.end()) {
|
||||||
material.normalTexture = getTexture(gltfModel.textures[mat.additionalValues["normalTexture"].TextureIndex()].source);
|
material.normalTexture = getTexture(gltfModel.textures[mat.additionalValues["normalTexture"].TextureIndex()].source);
|
||||||
|
} else {
|
||||||
|
material.normalTexture = &emptyTexture;
|
||||||
}
|
}
|
||||||
if (mat.additionalValues.find("emissiveTexture") != mat.additionalValues.end()) {
|
if (mat.additionalValues.find("emissiveTexture") != mat.additionalValues.end()) {
|
||||||
material.emissiveTexture = getTexture(gltfModel.textures[mat.additionalValues["emissiveTexture"].TextureIndex()].source);
|
material.emissiveTexture = getTexture(gltfModel.textures[mat.additionalValues["emissiveTexture"].TextureIndex()].source);
|
||||||
|
|
@ -911,7 +1033,6 @@ void vkglTF::Model::loadMaterials(tinygltf::Model &gltfModel)
|
||||||
}
|
}
|
||||||
// Push a default material at the end of the list for meshes with no material assigned
|
// Push a default material at the end of the list for meshes with no material assigned
|
||||||
materials.push_back(Material(device));
|
materials.push_back(Material(device));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void vkglTF::Model::loadAnimations(tinygltf::Model &gltfModel)
|
void vkglTF::Model::loadAnimations(tinygltf::Model &gltfModel)
|
||||||
|
|
@ -1210,7 +1331,12 @@ void vkglTF::Model::loadFromFile(std::string filename, vks::VulkanDevice *device
|
||||||
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, uboCount },
|
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, uboCount },
|
||||||
};
|
};
|
||||||
if (imageCount > 0) {
|
if (imageCount > 0) {
|
||||||
poolSizes.push_back({ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, imageCount });
|
if (descriptorBindingFlags & DescriptorBindingFlags::ImageBaseColor) {
|
||||||
|
poolSizes.push_back({ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, imageCount });
|
||||||
|
}
|
||||||
|
if (descriptorBindingFlags & DescriptorBindingFlags::ImageNormalMap) {
|
||||||
|
poolSizes.push_back({ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, imageCount });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
VkDescriptorPoolCreateInfo descriptorPoolCI{};
|
VkDescriptorPoolCreateInfo descriptorPoolCI{};
|
||||||
descriptorPoolCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
descriptorPoolCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
||||||
|
|
@ -1241,9 +1367,13 @@ void vkglTF::Model::loadFromFile(std::string filename, vks::VulkanDevice *device
|
||||||
{
|
{
|
||||||
// Layout is global, so only create if it hasn't already been created before
|
// Layout is global, so only create if it hasn't already been created before
|
||||||
if (descriptorSetLayoutImage == VK_NULL_HANDLE) {
|
if (descriptorSetLayoutImage == VK_NULL_HANDLE) {
|
||||||
std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings = {
|
std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings{};
|
||||||
vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 0),
|
if (descriptorBindingFlags & DescriptorBindingFlags::ImageBaseColor) {
|
||||||
};
|
setLayoutBindings.push_back(vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, static_cast<uint32_t>(setLayoutBindings.size())));
|
||||||
|
}
|
||||||
|
if (descriptorBindingFlags & DescriptorBindingFlags::ImageNormalMap) {
|
||||||
|
setLayoutBindings.push_back(vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, static_cast<uint32_t>(setLayoutBindings.size())));
|
||||||
|
}
|
||||||
VkDescriptorSetLayoutCreateInfo descriptorLayoutCI{};
|
VkDescriptorSetLayoutCreateInfo descriptorLayoutCI{};
|
||||||
descriptorLayoutCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
|
descriptorLayoutCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
|
||||||
descriptorLayoutCI.bindingCount = static_cast<uint32_t>(setLayoutBindings.size());
|
descriptorLayoutCI.bindingCount = static_cast<uint32_t>(setLayoutBindings.size());
|
||||||
|
|
@ -1252,11 +1382,10 @@ void vkglTF::Model::loadFromFile(std::string filename, vks::VulkanDevice *device
|
||||||
}
|
}
|
||||||
for (auto& material : materials) {
|
for (auto& material : materials) {
|
||||||
if (material.baseColorTexture != nullptr) {
|
if (material.baseColorTexture != nullptr) {
|
||||||
material.createDescriptorSet(descriptorPool, vkglTF::descriptorSetLayoutImage);
|
material.createDescriptorSet(descriptorPool, vkglTF::descriptorSetLayoutImage, descriptorBindingFlags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void vkglTF::Model::bindBuffers(VkCommandBuffer commandBuffer)
|
void vkglTF::Model::bindBuffers(VkCommandBuffer commandBuffer)
|
||||||
|
|
@ -1270,15 +1399,28 @@ void vkglTF::Model::bindBuffers(VkCommandBuffer commandBuffer)
|
||||||
void vkglTF::Model::drawNode(Node *node, VkCommandBuffer commandBuffer, uint32_t renderFlags, VkPipelineLayout pipelineLayout, uint32_t bindImageSet)
|
void vkglTF::Model::drawNode(Node *node, VkCommandBuffer commandBuffer, uint32_t renderFlags, VkPipelineLayout pipelineLayout, uint32_t bindImageSet)
|
||||||
{
|
{
|
||||||
if (node->mesh) {
|
if (node->mesh) {
|
||||||
for (Primitive *primitive : node->mesh->primitives) {
|
for (Primitive* primitive : node->mesh->primitives) {
|
||||||
if (renderFlags & vkglTF::RenderFlags::BindImages) {
|
bool skip = false;
|
||||||
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, bindImageSet, 1, &primitive->material.descriptorSet, 0, nullptr);
|
const vkglTF::Material& material = primitive->material;
|
||||||
|
if (renderFlags & RenderFlags::RenderOpaqueNodes) {
|
||||||
|
skip = (material.alphaMode != Material::ALPHAMODE_OPAQUE);
|
||||||
|
}
|
||||||
|
if (renderFlags & RenderFlags::RenderAlphaMaskedNodes) {
|
||||||
|
skip = (material.alphaMode != Material::ALPHAMODE_MASK);
|
||||||
|
}
|
||||||
|
if (renderFlags & RenderFlags::RenderAlphaBlendedNodes) {
|
||||||
|
skip = (material.alphaMode != Material::ALPHAMODE_BLEND);
|
||||||
|
}
|
||||||
|
if (!skip) {
|
||||||
|
if (renderFlags & RenderFlags::BindImages) {
|
||||||
|
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, bindImageSet, 1, &material.descriptorSet, 0, nullptr);
|
||||||
|
}
|
||||||
|
vkCmdDrawIndexed(commandBuffer, primitive->indexCount, 1, primitive->firstIndex, 0, 0);
|
||||||
}
|
}
|
||||||
vkCmdDrawIndexed(commandBuffer, primitive->indexCount, 1, primitive->firstIndex, 0, 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (auto& child : node->children) {
|
for (auto& child : node->children) {
|
||||||
drawNode(child, commandBuffer);
|
drawNode(child, commandBuffer, renderFlags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,9 +37,15 @@
|
||||||
|
|
||||||
namespace vkglTF
|
namespace vkglTF
|
||||||
{
|
{
|
||||||
|
enum DescriptorBindingFlags {
|
||||||
|
ImageBaseColor = 0x00000001,
|
||||||
|
ImageNormalMap = 0x00000002
|
||||||
|
};
|
||||||
|
|
||||||
extern VkDescriptorSetLayout descriptorSetLayoutImage;
|
extern VkDescriptorSetLayout descriptorSetLayoutImage;
|
||||||
extern VkDescriptorSetLayout descriptorSetLayoutUbo;
|
extern VkDescriptorSetLayout descriptorSetLayoutUbo;
|
||||||
extern VkMemoryPropertyFlags memoryPropertyFlags;
|
extern VkMemoryPropertyFlags memoryPropertyFlags;
|
||||||
|
extern uint32_t descriptorBindingFlags;
|
||||||
|
|
||||||
struct Node;
|
struct Node;
|
||||||
|
|
||||||
|
|
@ -85,7 +91,7 @@ namespace vkglTF
|
||||||
VkDescriptorSet descriptorSet = VK_NULL_HANDLE;
|
VkDescriptorSet descriptorSet = VK_NULL_HANDLE;
|
||||||
|
|
||||||
Material(vks::VulkanDevice* device) : device(device) {};
|
Material(vks::VulkanDevice* device) : device(device) {};
|
||||||
void createDescriptorSet(VkDescriptorPool descriptorPool, VkDescriptorSetLayout descriptorSetLayout);
|
void createDescriptorSet(VkDescriptorPool descriptorPool, VkDescriptorSetLayout descriptorSetLayout, uint32_t descriptorBindingFlags);
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -231,7 +237,10 @@ namespace vkglTF
|
||||||
};
|
};
|
||||||
|
|
||||||
enum RenderFlags {
|
enum RenderFlags {
|
||||||
BindImages = 0x00000001
|
BindImages = 0x00000001,
|
||||||
|
RenderOpaqueNodes = 0x00000002,
|
||||||
|
RenderAlphaMaskedNodes = 0x00000004,
|
||||||
|
RenderAlphaBlendedNodes = 0x00000008
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -240,6 +249,8 @@ namespace vkglTF
|
||||||
class Model {
|
class Model {
|
||||||
private:
|
private:
|
||||||
vkglTF::Texture* getTexture(uint32_t index);
|
vkglTF::Texture* getTexture(uint32_t index);
|
||||||
|
vkglTF::Texture emptyTexture;
|
||||||
|
void createEmptyTexture(VkQueue transferQueue);
|
||||||
public:
|
public:
|
||||||
vks::VulkanDevice* device;
|
vks::VulkanDevice* device;
|
||||||
VkDescriptorPool descriptorPool;
|
VkDescriptorPool descriptorPool;
|
||||||
|
|
|
||||||
84
data/shaders/glsl/variablerateshading/scene.frag
Normal file
84
data/shaders/glsl/variablerateshading/scene.frag
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
#version 450
|
||||||
|
|
||||||
|
#extension GL_NV_shading_rate_image : require
|
||||||
|
|
||||||
|
layout (set = 1, binding = 0) uniform sampler2D samplerColorMap;
|
||||||
|
layout (set = 1, binding = 1) uniform sampler2D samplerNormalMap;
|
||||||
|
|
||||||
|
layout (location = 0) in vec3 inNormal;
|
||||||
|
layout (location = 1) in vec3 inColor;
|
||||||
|
layout (location = 2) in vec2 inUV;
|
||||||
|
layout (location = 3) in vec3 inViewVec;
|
||||||
|
layout (location = 4) in vec3 inLightVec;
|
||||||
|
layout (location = 5) in vec4 inTangent;
|
||||||
|
|
||||||
|
layout (set = 0, binding = 0) uniform UBOScene
|
||||||
|
{
|
||||||
|
mat4 projection;
|
||||||
|
mat4 view;
|
||||||
|
mat4 model;
|
||||||
|
vec4 lightPos;
|
||||||
|
vec4 viewPos;
|
||||||
|
int colorShadingRates;
|
||||||
|
} uboScene;
|
||||||
|
|
||||||
|
layout (location = 0) out vec4 outFragColor;
|
||||||
|
|
||||||
|
layout (constant_id = 0) const bool ALPHA_MASK = false;
|
||||||
|
layout (constant_id = 1) const float ALPHA_MASK_CUTOFF = 0.0f;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec4 color = texture(samplerColorMap, inUV) * vec4(inColor, 1.0);
|
||||||
|
|
||||||
|
if (ALPHA_MASK) {
|
||||||
|
if (color.a < ALPHA_MASK_CUTOFF) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 N = normalize(inNormal);
|
||||||
|
vec3 T = normalize(inTangent.xyz);
|
||||||
|
vec3 B = cross(inNormal, inTangent.xyz) * inTangent.w;
|
||||||
|
mat3 TBN = mat3(T, B, N);
|
||||||
|
N = TBN * normalize(texture(samplerNormalMap, inUV).xyz * 2.0 - vec3(1.0));
|
||||||
|
|
||||||
|
const float ambient = 0.25;
|
||||||
|
vec3 L = normalize(inLightVec);
|
||||||
|
vec3 V = normalize(inViewVec);
|
||||||
|
vec3 R = reflect(-L, N);
|
||||||
|
vec3 diffuse = max(dot(N, L), ambient).rrr;
|
||||||
|
float specular = pow(max(dot(R, V), 0.0), 32.0);
|
||||||
|
outFragColor = vec4(diffuse * color.rgb + specular, color.a);
|
||||||
|
|
||||||
|
if (uboScene.colorShadingRates == 1) {
|
||||||
|
if (gl_FragmentSizeNV.x == 1 && gl_FragmentSizeNV.y == 1) {
|
||||||
|
outFragColor.rgb *= vec3(0.0, 0.8, 0.4);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (gl_FragmentSizeNV.x == 2 && gl_FragmentSizeNV.y == 1) {
|
||||||
|
outFragColor.rgb *= vec3(0.2, 0.6, 1.0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (gl_FragmentSizeNV.x == 1 && gl_FragmentSizeNV.y == 2) {
|
||||||
|
outFragColor.rgb *= vec3(0.0, 0.4, 0.8);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (gl_FragmentSizeNV.x == 2 && gl_FragmentSizeNV.y == 2) {
|
||||||
|
outFragColor.rgb *= vec3(1.0, 1.0, 0.2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (gl_FragmentSizeNV.x == 4 && gl_FragmentSizeNV.y == 2) {
|
||||||
|
outFragColor.rgb *= vec3(0.8, 0.8, 0.0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (gl_FragmentSizeNV.x == 2 && gl_FragmentSizeNV.y == 4) {
|
||||||
|
outFragColor.rgb *= vec3(1.0, 0.4, 0.2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (gl_FragmentSizeNV.x == 4 && gl_FragmentSizeNV.y == 4) {
|
||||||
|
outFragColor.rgb *= vec3(0.8, 0.0, 0.0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
data/shaders/glsl/variablerateshading/scene.frag.spv
Normal file
BIN
data/shaders/glsl/variablerateshading/scene.frag.spv
Normal file
Binary file not shown.
38
data/shaders/glsl/variablerateshading/scene.vert
Normal file
38
data/shaders/glsl/variablerateshading/scene.vert
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
#version 450
|
||||||
|
|
||||||
|
layout (location = 0) in vec3 inPos;
|
||||||
|
layout (location = 1) in vec3 inNormal;
|
||||||
|
layout (location = 2) in vec2 inUV;
|
||||||
|
layout (location = 3) in vec3 inColor;
|
||||||
|
layout (location = 4) in vec4 inTangent;
|
||||||
|
|
||||||
|
layout (set = 0, binding = 0) uniform UBOScene
|
||||||
|
{
|
||||||
|
mat4 projection;
|
||||||
|
mat4 view;
|
||||||
|
mat4 model;
|
||||||
|
vec4 lightPos;
|
||||||
|
vec4 viewPos;
|
||||||
|
int colorShadingRates;
|
||||||
|
} uboScene;
|
||||||
|
|
||||||
|
layout (location = 0) out vec3 outNormal;
|
||||||
|
layout (location = 1) out vec3 outColor;
|
||||||
|
layout (location = 2) out vec2 outUV;
|
||||||
|
layout (location = 3) out vec3 outViewVec;
|
||||||
|
layout (location = 4) out vec3 outLightVec;
|
||||||
|
layout (location = 5) out vec4 outTangent;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
outNormal = inNormal;
|
||||||
|
outColor = inColor;
|
||||||
|
outUV = inUV;
|
||||||
|
outTangent = inTangent;
|
||||||
|
gl_Position = uboScene.projection * uboScene.view * uboScene.model * vec4(inPos.xyz, 1.0);
|
||||||
|
|
||||||
|
outNormal = mat3(uboScene.model) * inNormal;
|
||||||
|
vec4 pos = uboScene.model * vec4(inPos, 1.0);
|
||||||
|
outLightVec = uboScene.lightPos.xyz - pos.xyz;
|
||||||
|
outViewVec = uboScene.viewPos.xyz - pos.xyz;
|
||||||
|
}
|
||||||
BIN
data/shaders/glsl/variablerateshading/scene.vert.spv
Normal file
BIN
data/shaders/glsl/variablerateshading/scene.vert.spv
Normal file
Binary file not shown.
83
data/shaders/hlsl/variablerateshading/scene.frag
Normal file
83
data/shaders/hlsl/variablerateshading/scene.frag
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
// Copyright 2020 Sascha Willems
|
||||||
|
|
||||||
|
Texture2D textureColorMap : register(t0, space1);
|
||||||
|
SamplerState samplerColorMap : register(s0, space1);
|
||||||
|
Texture2D textureNormalMap : register(t1, space1);
|
||||||
|
SamplerState samplerNormalMap : register(s1, space1);
|
||||||
|
|
||||||
|
struct UBO
|
||||||
|
{
|
||||||
|
float4x4 projection;
|
||||||
|
float4x4 view;
|
||||||
|
float4x4 model;
|
||||||
|
float4 lightPos;
|
||||||
|
float4 viewPos;
|
||||||
|
int colorShadingRates;
|
||||||
|
};
|
||||||
|
cbuffer ubo : register(b0) { UBO ubo; };
|
||||||
|
|
||||||
|
[[vk::constant_id(0)]] const bool ALPHA_MASK = false;
|
||||||
|
[[vk::constant_id(1)]] const float ALPHA_MASK_CUTOFF = 0.0;
|
||||||
|
|
||||||
|
struct VSOutput
|
||||||
|
{
|
||||||
|
[[vk::location(0)]] float3 Normal : NORMAL0;
|
||||||
|
[[vk::location(1)]] float3 Color : COLOR0;
|
||||||
|
[[vk::location(2)]] float2 UV : TEXCOORD0;
|
||||||
|
[[vk::location(3)]] float3 ViewVec : TEXCOORD1;
|
||||||
|
[[vk::location(4)]] float3 LightVec : TEXCOORD2;
|
||||||
|
[[vk::location(5)]] float4 Tangent : TEXCOORD3;
|
||||||
|
};
|
||||||
|
|
||||||
|
float4 main(VSOutput input, uint shadingRate : SV_ShadingRate) : SV_TARGET
|
||||||
|
{
|
||||||
|
float4 color = textureColorMap.Sample(samplerColorMap, input.UV) * float4(input.Color, 1.0);
|
||||||
|
|
||||||
|
if (ALPHA_MASK) {
|
||||||
|
if (color.a < ALPHA_MASK_CUTOFF) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float3 N = normalize(input.Normal);
|
||||||
|
float3 T = normalize(input.Tangent.xyz);
|
||||||
|
float3 B = cross(input.Normal, input.Tangent.xyz) * input.Tangent.w;
|
||||||
|
float3x3 TBN = float3x3(T, B, N);
|
||||||
|
N = mul(normalize(textureNormalMap.Sample(samplerNormalMap, input.UV).xyz * 2.0 - float3(1.0, 1.0, 1.0)), TBN);
|
||||||
|
|
||||||
|
const float ambient = 0.1;
|
||||||
|
float3 L = normalize(input.LightVec);
|
||||||
|
float3 V = normalize(input.ViewVec);
|
||||||
|
float3 R = reflect(-L, N);
|
||||||
|
float3 diffuse = max(dot(N, L), ambient).rrr;
|
||||||
|
float3 specular = pow(max(dot(R, V), 0.0), 32.0);
|
||||||
|
color = float4(diffuse * color.rgb + specular, color.a);
|
||||||
|
|
||||||
|
const uint SHADING_RATE_PER_PIXEL = 0x0;
|
||||||
|
const uint SHADING_RATE_PER_2X1_PIXELS = 6;
|
||||||
|
const uint SHADING_RATE_PER_1X2_PIXELS = 7;
|
||||||
|
const uint SHADING_RATE_PER_2X2_PIXELS = 8;
|
||||||
|
const uint SHADING_RATE_PER_4X2_PIXELS = 9;
|
||||||
|
const uint SHADING_RATE_PER_2X4_PIXELS = 10;
|
||||||
|
|
||||||
|
if (ubo.colorShadingRates == 1) {
|
||||||
|
switch(shadingRate) {
|
||||||
|
case SHADING_RATE_PER_PIXEL:
|
||||||
|
return color * float4(0.0, 0.8, 0.4, 1.0);
|
||||||
|
case SHADING_RATE_PER_2X1_PIXELS:
|
||||||
|
return color * float4(0.2, 0.6, 1.0, 1.0);
|
||||||
|
case SHADING_RATE_PER_1X2_PIXELS:
|
||||||
|
return color * float4(0.0, 0.4, 0.8, 1.0);
|
||||||
|
case SHADING_RATE_PER_2X2_PIXELS:
|
||||||
|
return color * float4(1.0, 1.0, 0.2, 1.0);
|
||||||
|
case SHADING_RATE_PER_4X2_PIXELS:
|
||||||
|
return color * float4(0.8, 0.8, 0.0, 1.0);
|
||||||
|
case SHADING_RATE_PER_2X4_PIXELS:
|
||||||
|
return color * float4(1.0, 0.4, 0.2, 1.0);
|
||||||
|
default:
|
||||||
|
return color * float4(0.8, 0.0, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return color;
|
||||||
|
}
|
||||||
BIN
data/shaders/hlsl/variablerateshading/scene.frag.spv
Normal file
BIN
data/shaders/hlsl/variablerateshading/scene.frag.spv
Normal file
Binary file not shown.
51
data/shaders/hlsl/variablerateshading/scene.vert
Normal file
51
data/shaders/hlsl/variablerateshading/scene.vert
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
// Copyright 2020 Google LLC
|
||||||
|
|
||||||
|
struct VSInput
|
||||||
|
{
|
||||||
|
[[vk::location(0)]] float3 Pos : POSITION0;
|
||||||
|
[[vk::location(1)]] float3 Normal : NORMAL0;
|
||||||
|
[[vk::location(2)]] float2 UV : TEXCOORD0;
|
||||||
|
[[vk::location(3)]] float3 Color : COLOR0;
|
||||||
|
[[vk::location(4)]] float4 Tangent : TEXCOORD1;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UBO
|
||||||
|
{
|
||||||
|
float4x4 projection;
|
||||||
|
float4x4 view;
|
||||||
|
float4x4 model;
|
||||||
|
float4 lightPos;
|
||||||
|
float4 viewPos;
|
||||||
|
int colorShadingRates;
|
||||||
|
};
|
||||||
|
cbuffer ubo : register(b0) { UBO ubo; };
|
||||||
|
|
||||||
|
struct VSOutput
|
||||||
|
{
|
||||||
|
float4 Pos : SV_POSITION;
|
||||||
|
[[vk::location(0)]] float3 Normal : NORMAL0;
|
||||||
|
[[vk::location(1)]] float3 Color : COLOR0;
|
||||||
|
[[vk::location(2)]] float2 UV : TEXCOORD0;
|
||||||
|
[[vk::location(3)]] float3 ViewVec : TEXCOORD1;
|
||||||
|
[[vk::location(4)]] float3 LightVec : TEXCOORD2;
|
||||||
|
[[vk::location(5)]] float4 Tangent : TEXCOORD3;
|
||||||
|
};
|
||||||
|
|
||||||
|
VSOutput main(VSInput input)
|
||||||
|
{
|
||||||
|
VSOutput output = (VSOutput)0;
|
||||||
|
output.Normal = input.Normal;
|
||||||
|
output.Color = input.Color;
|
||||||
|
output.UV = input.UV;
|
||||||
|
output.Tangent = input.Tangent;
|
||||||
|
|
||||||
|
float4x4 modelView = mul(ubo.view, ubo.model);
|
||||||
|
|
||||||
|
output.Pos = mul(ubo.projection, mul(modelView, float4(input.Pos.xyz, 1.0)));
|
||||||
|
|
||||||
|
output.Normal = mul((float3x3)ubo.model, input.Normal);
|
||||||
|
float4 pos = mul(ubo.model, float4(input.Pos, 1.0));
|
||||||
|
output.LightVec = ubo.lightPos.xyz - pos.xyz;
|
||||||
|
output.ViewVec = ubo.viewPos.xyz - pos.xyz;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
BIN
data/shaders/hlsl/variablerateshading/scene.vert.spv
Normal file
BIN
data/shaders/hlsl/variablerateshading/scene.vert.spv
Normal file
Binary file not shown.
|
|
@ -122,6 +122,7 @@ set(EXAMPLES
|
||||||
texturemipmapgen
|
texturemipmapgen
|
||||||
texturesparseresidency
|
texturesparseresidency
|
||||||
triangle
|
triangle
|
||||||
|
variablerateshading
|
||||||
viewportarray
|
viewportarray
|
||||||
vulkanscene
|
vulkanscene
|
||||||
)
|
)
|
||||||
|
|
|
||||||
439
examples/variablerateshading/variablerateshading.cpp
Normal file
439
examples/variablerateshading/variablerateshading.cpp
Normal file
|
|
@ -0,0 +1,439 @@
|
||||||
|
/*
|
||||||
|
* Vulkan Example - Variable rate shading
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 by Sascha Willems - www.saschawillems.de
|
||||||
|
*
|
||||||
|
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "variablerateshading.h"
|
||||||
|
|
||||||
|
VulkanExample::VulkanExample() : VulkanExampleBase(ENABLE_VALIDATION)
|
||||||
|
{
|
||||||
|
title = "Variable rate shading";
|
||||||
|
apiVersion = VK_VERSION_1_1;
|
||||||
|
camera.type = Camera::CameraType::firstperson;
|
||||||
|
camera.flipY = true;
|
||||||
|
camera.setPosition(glm::vec3(0.0f, 1.0f, 0.0f));
|
||||||
|
camera.setRotation(glm::vec3(0.0f, -90.0f, 0.0f));
|
||||||
|
camera.setPerspective(60.0f, (float)width / (float)height, 0.1f, 256.0f);
|
||||||
|
camera.setRotationSpeed(0.25f);
|
||||||
|
settings.overlay = true;
|
||||||
|
enabledInstanceExtensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
|
||||||
|
enabledDeviceExtensions.push_back(VK_NV_SHADING_RATE_IMAGE_EXTENSION_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
VulkanExample::~VulkanExample()
|
||||||
|
{
|
||||||
|
vkDestroyPipeline(device, basePipelines.masked, nullptr);
|
||||||
|
vkDestroyPipeline(device, basePipelines.opaque, nullptr);
|
||||||
|
vkDestroyPipeline(device, shadingRatePipelines.masked, nullptr);
|
||||||
|
vkDestroyPipeline(device, shadingRatePipelines.opaque, nullptr);
|
||||||
|
vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
|
||||||
|
vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);
|
||||||
|
vkDestroyImageView(device, shadingRateImage.view, nullptr);
|
||||||
|
vkDestroyImage(device, shadingRateImage.image, nullptr);
|
||||||
|
vkFreeMemory(device, shadingRateImage.memory, nullptr);
|
||||||
|
shaderData.buffer.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanExample::getEnabledFeatures()
|
||||||
|
{
|
||||||
|
enabledFeatures.samplerAnisotropy = deviceFeatures.samplerAnisotropy;
|
||||||
|
// POI
|
||||||
|
enabledPhysicalDeviceShadingRateImageFeaturesNV = {};
|
||||||
|
enabledPhysicalDeviceShadingRateImageFeaturesNV.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADING_RATE_IMAGE_FEATURES_NV;
|
||||||
|
enabledPhysicalDeviceShadingRateImageFeaturesNV.shadingRateImage = VK_TRUE;
|
||||||
|
deviceCreatepNextChain = &enabledPhysicalDeviceShadingRateImageFeaturesNV;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
If the window has been resized, we need to recreate the shading rate image
|
||||||
|
*/
|
||||||
|
void VulkanExample::handleResize()
|
||||||
|
{
|
||||||
|
// Delete allocated resources
|
||||||
|
vkDestroyImageView(device, shadingRateImage.view, nullptr);
|
||||||
|
vkDestroyImage(device, shadingRateImage.image, nullptr);
|
||||||
|
vkFreeMemory(device, shadingRateImage.memory, nullptr);
|
||||||
|
// Recreate image
|
||||||
|
prepareShadingRateImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanExample::buildCommandBuffers()
|
||||||
|
{
|
||||||
|
if (resized)
|
||||||
|
{
|
||||||
|
handleResize();
|
||||||
|
}
|
||||||
|
|
||||||
|
VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo();
|
||||||
|
|
||||||
|
VkClearValue clearValues[2];
|
||||||
|
clearValues[0].color = defaultClearColor;
|
||||||
|
clearValues[0].color = { { 0.25f, 0.25f, 0.25f, 1.0f } };;
|
||||||
|
clearValues[1].depthStencil = { 1.0f, 0 };
|
||||||
|
|
||||||
|
VkRenderPassBeginInfo renderPassBeginInfo = vks::initializers::renderPassBeginInfo();
|
||||||
|
renderPassBeginInfo.renderPass = renderPass;
|
||||||
|
renderPassBeginInfo.renderArea.offset.x = 0;
|
||||||
|
renderPassBeginInfo.renderArea.offset.y = 0;
|
||||||
|
renderPassBeginInfo.renderArea.extent.width = width;
|
||||||
|
renderPassBeginInfo.renderArea.extent.height = height;
|
||||||
|
renderPassBeginInfo.clearValueCount = 2;
|
||||||
|
renderPassBeginInfo.pClearValues = clearValues;
|
||||||
|
|
||||||
|
const VkViewport viewport = vks::initializers::viewport((float)width, (float)height, 0.0f, 1.0f);
|
||||||
|
const VkRect2D scissor = vks::initializers::rect2D(width, height, 0, 0);
|
||||||
|
|
||||||
|
for (int32_t i = 0; i < drawCmdBuffers.size(); ++i)
|
||||||
|
{
|
||||||
|
renderPassBeginInfo.framebuffer = frameBuffers[i];
|
||||||
|
VK_CHECK_RESULT(vkBeginCommandBuffer(drawCmdBuffers[i], &cmdBufInfo));
|
||||||
|
vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
|
||||||
|
vkCmdSetViewport(drawCmdBuffers[i], 0, 1, &viewport);
|
||||||
|
vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor);
|
||||||
|
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, nullptr);
|
||||||
|
|
||||||
|
// POI: Bind the image that contains the shading rate patterns
|
||||||
|
if (enableShadingRate) {
|
||||||
|
vkCmdBindShadingRateImageNV(drawCmdBuffers[i], shadingRateImage.view, VK_IMAGE_LAYOUT_SHADING_RATE_OPTIMAL_NV);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Render the scene
|
||||||
|
Pipelines& pipelines = enableShadingRate ? shadingRatePipelines : basePipelines;
|
||||||
|
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.opaque);
|
||||||
|
scene.draw(drawCmdBuffers[i], vkglTF::RenderFlags::BindImages | vkglTF::RenderFlags::RenderOpaqueNodes, pipelineLayout);
|
||||||
|
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.masked);
|
||||||
|
scene.draw(drawCmdBuffers[i], vkglTF::RenderFlags::BindImages | vkglTF::RenderFlags::RenderAlphaMaskedNodes, pipelineLayout);
|
||||||
|
|
||||||
|
drawUI(drawCmdBuffers[i]);
|
||||||
|
vkCmdEndRenderPass(drawCmdBuffers[i]);
|
||||||
|
VK_CHECK_RESULT(vkEndCommandBuffer(drawCmdBuffers[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanExample::loadAssets()
|
||||||
|
{
|
||||||
|
vkglTF::descriptorBindingFlags = vkglTF::DescriptorBindingFlags::ImageBaseColor | vkglTF::DescriptorBindingFlags::ImageNormalMap;
|
||||||
|
scene.loadFromFile(getAssetPath() + "models/sponza/sponza.gltf", vulkanDevice, queue, vkglTF::FileLoadingFlags::PreTransformVertices);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanExample::setupDescriptors()
|
||||||
|
{
|
||||||
|
// Pool
|
||||||
|
const std::vector<VkDescriptorPoolSize> poolSizes = {
|
||||||
|
vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1),
|
||||||
|
};
|
||||||
|
VkDescriptorPoolCreateInfo descriptorPoolInfo = vks::initializers::descriptorPoolCreateInfo(poolSizes, 1);
|
||||||
|
VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool));
|
||||||
|
|
||||||
|
// Descriptor set layout
|
||||||
|
const std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings = {
|
||||||
|
vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0),
|
||||||
|
};
|
||||||
|
VkDescriptorSetLayoutCreateInfo descriptorLayout = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings);
|
||||||
|
VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout));
|
||||||
|
|
||||||
|
// Pipeline layout
|
||||||
|
const std::vector<VkDescriptorSetLayout> setLayouts = {
|
||||||
|
descriptorSetLayout,
|
||||||
|
vkglTF::descriptorSetLayoutImage,
|
||||||
|
};
|
||||||
|
VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = vks::initializers::pipelineLayoutCreateInfo(setLayouts.data(), 2);
|
||||||
|
VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pPipelineLayoutCreateInfo, nullptr, &pipelineLayout));
|
||||||
|
|
||||||
|
// Descriptor set
|
||||||
|
VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayout, 1);
|
||||||
|
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet));
|
||||||
|
std::vector<VkWriteDescriptorSet> writeDescriptorSets = {
|
||||||
|
vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &shaderData.buffer.descriptor),
|
||||||
|
};
|
||||||
|
vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// [POI]
|
||||||
|
void VulkanExample::prepareShadingRateImage()
|
||||||
|
{
|
||||||
|
// Shading rate image size depends on shading rate texel size
|
||||||
|
// For each texel in the target image, there is a corresponding shading texel size width x height block in the shading rate image
|
||||||
|
VkExtent3D imageExtent{};
|
||||||
|
imageExtent.width = static_cast<uint32_t>(ceil(width / (float)physicalDeviceShadingRateImagePropertiesNV.shadingRateTexelSize.width));
|
||||||
|
imageExtent.height = static_cast<uint32_t>(ceil(height / (float)physicalDeviceShadingRateImagePropertiesNV.shadingRateTexelSize.height));
|
||||||
|
imageExtent.depth = 1;
|
||||||
|
|
||||||
|
VkImageCreateInfo imageCI{};
|
||||||
|
imageCI.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||||
|
imageCI.imageType = VK_IMAGE_TYPE_2D;
|
||||||
|
imageCI.format = VK_FORMAT_R8_UINT;
|
||||||
|
imageCI.extent = imageExtent;
|
||||||
|
imageCI.mipLevels = 1;
|
||||||
|
imageCI.arrayLayers = 1;
|
||||||
|
imageCI.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||||
|
imageCI.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||||
|
imageCI.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||||
|
imageCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||||
|
imageCI.usage = VK_IMAGE_USAGE_SHADING_RATE_IMAGE_BIT_NV | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||||
|
VK_CHECK_RESULT(vkCreateImage(device, &imageCI, nullptr, &shadingRateImage.image));
|
||||||
|
VkMemoryRequirements memReqs{};
|
||||||
|
vkGetImageMemoryRequirements(device, shadingRateImage.image, &memReqs);
|
||||||
|
|
||||||
|
VkDeviceSize bufferSize = imageExtent.width * imageExtent.height * sizeof(uint8_t);
|
||||||
|
|
||||||
|
VkMemoryAllocateInfo memAllloc{};
|
||||||
|
memAllloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||||
|
memAllloc.allocationSize = memReqs.size;
|
||||||
|
memAllloc.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||||
|
VK_CHECK_RESULT(vkAllocateMemory(device, &memAllloc, nullptr, &shadingRateImage.memory));
|
||||||
|
VK_CHECK_RESULT(vkBindImageMemory(device, shadingRateImage.image, shadingRateImage.memory, 0));
|
||||||
|
|
||||||
|
VkImageViewCreateInfo imageViewCI{};
|
||||||
|
imageViewCI.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||||
|
imageViewCI.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||||
|
imageViewCI.image = shadingRateImage.image;
|
||||||
|
imageViewCI.format = VK_FORMAT_R8_UINT;
|
||||||
|
imageViewCI.subresourceRange.baseMipLevel = 0;
|
||||||
|
imageViewCI.subresourceRange.levelCount = 1;
|
||||||
|
imageViewCI.subresourceRange.baseArrayLayer = 0;
|
||||||
|
imageViewCI.subresourceRange.layerCount = 1;
|
||||||
|
imageViewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
|
VK_CHECK_RESULT(vkCreateImageView(device, &imageViewCI, nullptr, &shadingRateImage.view));
|
||||||
|
|
||||||
|
// Populate with lowest possible shading rate pattern
|
||||||
|
uint8_t val = VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_4X4_PIXELS_NV;
|
||||||
|
uint8_t* shadingRatePatternData = new uint8_t[bufferSize];
|
||||||
|
memset(shadingRatePatternData, val, bufferSize);
|
||||||
|
|
||||||
|
// Create a circular pattern with decreasing sampling rates outwards (max. range, pattern)
|
||||||
|
std::map<float, VkShadingRatePaletteEntryNV> patternLookup = {
|
||||||
|
{ 8.0f, VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_PIXEL_NV },
|
||||||
|
{ 12.0f, VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_2X1_PIXELS_NV },
|
||||||
|
{ 16.0f, VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_1X2_PIXELS_NV },
|
||||||
|
{ 18.0f, VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_2X2_PIXELS_NV },
|
||||||
|
{ 20.0f, VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_4X2_PIXELS_NV },
|
||||||
|
{ 24.0f, VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_2X4_PIXELS_NV }
|
||||||
|
};
|
||||||
|
|
||||||
|
uint8_t* ptrData = shadingRatePatternData;
|
||||||
|
for (uint32_t y = 0; y < imageExtent.height; y++) {
|
||||||
|
for (uint32_t x = 0; x < imageExtent.width; x++) {
|
||||||
|
const float deltaX = (float)imageExtent.width / 2.0f - (float)x;
|
||||||
|
const float deltaY = ((float)imageExtent.height / 2.0f - (float)y) * ((float)width / (float)height);
|
||||||
|
const float dist = std::sqrt(deltaX * deltaX + deltaY * deltaY);
|
||||||
|
for (auto pattern : patternLookup) {
|
||||||
|
if (dist < pattern.first) {
|
||||||
|
*ptrData = pattern.second;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ptrData++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VkBuffer stagingBuffer;
|
||||||
|
VkDeviceMemory stagingMemory;
|
||||||
|
|
||||||
|
VkBufferCreateInfo bufferCreateInfo{};
|
||||||
|
bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
||||||
|
bufferCreateInfo.size = bufferSize;
|
||||||
|
bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
|
||||||
|
bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||||
|
VK_CHECK_RESULT(vkCreateBuffer(device, &bufferCreateInfo, nullptr, &stagingBuffer));
|
||||||
|
VkMemoryAllocateInfo memAllocInfo{};
|
||||||
|
memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||||
|
memReqs = {};
|
||||||
|
vkGetBufferMemoryRequirements(device, stagingBuffer, &memReqs);
|
||||||
|
memAllocInfo.allocationSize = memReqs.size;
|
||||||
|
memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
|
||||||
|
VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &stagingMemory));
|
||||||
|
VK_CHECK_RESULT(vkBindBufferMemory(device, stagingBuffer, stagingMemory, 0));
|
||||||
|
|
||||||
|
uint8_t* mapped;
|
||||||
|
VK_CHECK_RESULT(vkMapMemory(device, stagingMemory, 0, memReqs.size, 0, (void**)&mapped));
|
||||||
|
memcpy(mapped, shadingRatePatternData, bufferSize);
|
||||||
|
vkUnmapMemory(device, stagingMemory);
|
||||||
|
|
||||||
|
delete[] shadingRatePatternData;
|
||||||
|
|
||||||
|
// Upload
|
||||||
|
VkCommandBuffer copyCmd = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
|
||||||
|
VkImageSubresourceRange subresourceRange = {};
|
||||||
|
subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
|
subresourceRange.levelCount = 1;
|
||||||
|
subresourceRange.layerCount = 1;
|
||||||
|
{
|
||||||
|
VkImageMemoryBarrier imageMemoryBarrier{};
|
||||||
|
imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||||
|
imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||||
|
imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||||
|
imageMemoryBarrier.srcAccessMask = 0;
|
||||||
|
imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||||
|
imageMemoryBarrier.image = shadingRateImage.image;
|
||||||
|
imageMemoryBarrier.subresourceRange = subresourceRange;
|
||||||
|
vkCmdPipelineBarrier(copyCmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier);
|
||||||
|
}
|
||||||
|
VkBufferImageCopy bufferCopyRegion{};
|
||||||
|
bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
|
bufferCopyRegion.imageSubresource.layerCount = 1;
|
||||||
|
bufferCopyRegion.imageExtent.width = imageExtent.width;
|
||||||
|
bufferCopyRegion.imageExtent.height = imageExtent.height;
|
||||||
|
bufferCopyRegion.imageExtent.depth = 1;
|
||||||
|
vkCmdCopyBufferToImage(copyCmd, stagingBuffer, shadingRateImage.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &bufferCopyRegion);
|
||||||
|
{
|
||||||
|
VkImageMemoryBarrier imageMemoryBarrier{};
|
||||||
|
imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||||
|
imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||||
|
imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_SHADING_RATE_OPTIMAL_NV;
|
||||||
|
imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||||
|
imageMemoryBarrier.dstAccessMask = 0;
|
||||||
|
imageMemoryBarrier.image = shadingRateImage.image;
|
||||||
|
imageMemoryBarrier.subresourceRange = subresourceRange;
|
||||||
|
vkCmdPipelineBarrier(copyCmd, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier);
|
||||||
|
}
|
||||||
|
vulkanDevice->flushCommandBuffer(copyCmd, queue, true);
|
||||||
|
|
||||||
|
vkFreeMemory(device, stagingMemory, nullptr);
|
||||||
|
vkDestroyBuffer(device, stagingBuffer, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanExample::preparePipelines()
|
||||||
|
{
|
||||||
|
VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCI = vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE);
|
||||||
|
VkPipelineRasterizationStateCreateInfo rasterizationStateCI = vks::initializers::pipelineRasterizationStateCreateInfo(VK_POLYGON_MODE_FILL, VK_CULL_MODE_BACK_BIT, VK_FRONT_FACE_COUNTER_CLOCKWISE, 0);
|
||||||
|
VkPipelineColorBlendAttachmentState blendAttachmentStateCI = vks::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE);
|
||||||
|
VkPipelineColorBlendStateCreateInfo colorBlendStateCI = vks::initializers::pipelineColorBlendStateCreateInfo(1, &blendAttachmentStateCI);
|
||||||
|
VkPipelineDepthStencilStateCreateInfo depthStencilStateCI = vks::initializers::pipelineDepthStencilStateCreateInfo(VK_TRUE, VK_TRUE, VK_COMPARE_OP_LESS_OR_EQUAL);
|
||||||
|
VkPipelineViewportStateCreateInfo viewportStateCI = vks::initializers::pipelineViewportStateCreateInfo(1, 1, 0);
|
||||||
|
VkPipelineMultisampleStateCreateInfo multisampleStateCI = vks::initializers::pipelineMultisampleStateCreateInfo(VK_SAMPLE_COUNT_1_BIT, 0);
|
||||||
|
const std::vector<VkDynamicState> dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
|
||||||
|
VkPipelineDynamicStateCreateInfo dynamicStateCI = vks::initializers::pipelineDynamicStateCreateInfo(dynamicStateEnables.data(), static_cast<uint32_t>(dynamicStateEnables.size()), 0);
|
||||||
|
std::array<VkPipelineShaderStageCreateInfo, 2> shaderStages;
|
||||||
|
|
||||||
|
VkGraphicsPipelineCreateInfo pipelineCI = vks::initializers::pipelineCreateInfo(pipelineLayout, renderPass, 0);
|
||||||
|
pipelineCI.pInputAssemblyState = &inputAssemblyStateCI;
|
||||||
|
pipelineCI.pRasterizationState = &rasterizationStateCI;
|
||||||
|
pipelineCI.pColorBlendState = &colorBlendStateCI;
|
||||||
|
pipelineCI.pMultisampleState = &multisampleStateCI;
|
||||||
|
pipelineCI.pViewportState = &viewportStateCI;
|
||||||
|
pipelineCI.pDepthStencilState = &depthStencilStateCI;
|
||||||
|
pipelineCI.pDynamicState = &dynamicStateCI;
|
||||||
|
pipelineCI.stageCount = static_cast<uint32_t>(shaderStages.size());
|
||||||
|
pipelineCI.pStages = shaderStages.data();
|
||||||
|
pipelineCI.pVertexInputState = vkglTF::Vertex::getPipelineVertexInputState({ vkglTF::VertexComponent::Position, vkglTF::VertexComponent::Normal, vkglTF::VertexComponent::UV, vkglTF::VertexComponent::Color, vkglTF::VertexComponent::Tangent });
|
||||||
|
|
||||||
|
shaderStages[0] = loadShader(getShadersPath() + "variablerateshading/scene.vert.spv", VK_SHADER_STAGE_VERTEX_BIT);
|
||||||
|
shaderStages[1] = loadShader(getShadersPath() + "variablerateshading/scene.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
|
||||||
|
|
||||||
|
// Properties for alpha masked materials will be passed via specialization constants
|
||||||
|
struct SpecializationData {
|
||||||
|
bool alphaMask;
|
||||||
|
float alphaMaskCutoff;
|
||||||
|
} specializationData;
|
||||||
|
specializationData.alphaMask = false;
|
||||||
|
specializationData.alphaMaskCutoff = 0.5f;
|
||||||
|
const std::vector<VkSpecializationMapEntry> specializationMapEntries = {
|
||||||
|
vks::initializers::specializationMapEntry(0, offsetof(SpecializationData, alphaMask), sizeof(SpecializationData::alphaMask)),
|
||||||
|
vks::initializers::specializationMapEntry(1, offsetof(SpecializationData, alphaMaskCutoff), sizeof(SpecializationData::alphaMaskCutoff)),
|
||||||
|
};
|
||||||
|
VkSpecializationInfo specializationInfo = vks::initializers::specializationInfo(specializationMapEntries, sizeof(specializationData), &specializationData);
|
||||||
|
shaderStages[1].pSpecializationInfo = &specializationInfo;
|
||||||
|
|
||||||
|
// Create pipeline without shading rate
|
||||||
|
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &basePipelines.opaque));
|
||||||
|
specializationData.alphaMask = true;
|
||||||
|
rasterizationStateCI.cullMode = VK_CULL_MODE_NONE;
|
||||||
|
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &basePipelines.masked));
|
||||||
|
rasterizationStateCI.cullMode = VK_CULL_MODE_BACK_BIT;
|
||||||
|
specializationData.alphaMask = false;
|
||||||
|
|
||||||
|
// Create pipeline with shading rate enabled
|
||||||
|
// [POI] Possible per-Viewport shading rate palette entries
|
||||||
|
const std::vector<VkShadingRatePaletteEntryNV> shadingRatePaletteEntries = {
|
||||||
|
VK_SHADING_RATE_PALETTE_ENTRY_NO_INVOCATIONS_NV,
|
||||||
|
VK_SHADING_RATE_PALETTE_ENTRY_16_INVOCATIONS_PER_PIXEL_NV,
|
||||||
|
VK_SHADING_RATE_PALETTE_ENTRY_8_INVOCATIONS_PER_PIXEL_NV,
|
||||||
|
VK_SHADING_RATE_PALETTE_ENTRY_4_INVOCATIONS_PER_PIXEL_NV,
|
||||||
|
VK_SHADING_RATE_PALETTE_ENTRY_2_INVOCATIONS_PER_PIXEL_NV,
|
||||||
|
VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_PIXEL_NV,
|
||||||
|
VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_2X1_PIXELS_NV,
|
||||||
|
VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_1X2_PIXELS_NV,
|
||||||
|
VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_2X2_PIXELS_NV,
|
||||||
|
VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_4X2_PIXELS_NV,
|
||||||
|
VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_2X4_PIXELS_NV,
|
||||||
|
VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_4X4_PIXELS_NV,
|
||||||
|
};
|
||||||
|
VkShadingRatePaletteNV shadingRatePalette{};
|
||||||
|
shadingRatePalette.shadingRatePaletteEntryCount = static_cast<uint32_t>(shadingRatePaletteEntries.size());
|
||||||
|
shadingRatePalette.pShadingRatePaletteEntries = shadingRatePaletteEntries.data();
|
||||||
|
VkPipelineViewportShadingRateImageStateCreateInfoNV pipelineViewportShadingRateImageStateCI{};
|
||||||
|
pipelineViewportShadingRateImageStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_SHADING_RATE_IMAGE_STATE_CREATE_INFO_NV;
|
||||||
|
pipelineViewportShadingRateImageStateCI.shadingRateImageEnable = VK_TRUE;
|
||||||
|
pipelineViewportShadingRateImageStateCI.viewportCount = 1;
|
||||||
|
pipelineViewportShadingRateImageStateCI.pShadingRatePalettes = &shadingRatePalette;
|
||||||
|
viewportStateCI.pNext = &pipelineViewportShadingRateImageStateCI;
|
||||||
|
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &shadingRatePipelines.opaque));
|
||||||
|
specializationData.alphaMask = true;
|
||||||
|
rasterizationStateCI.cullMode = VK_CULL_MODE_NONE;
|
||||||
|
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &shadingRatePipelines.masked));
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanExample::prepareUniformBuffers()
|
||||||
|
{
|
||||||
|
VK_CHECK_RESULT(vulkanDevice->createBuffer(
|
||||||
|
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
|
||||||
|
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
|
||||||
|
&shaderData.buffer,
|
||||||
|
sizeof(shaderData.values)));
|
||||||
|
VK_CHECK_RESULT(shaderData.buffer.map());
|
||||||
|
updateUniformBuffers();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanExample::updateUniformBuffers()
|
||||||
|
{
|
||||||
|
shaderData.values.projection = camera.matrices.perspective;
|
||||||
|
shaderData.values.view = camera.matrices.view;
|
||||||
|
shaderData.values.viewPos = camera.viewPos;
|
||||||
|
shaderData.values.colorShadingRate = colorShadingRate;
|
||||||
|
memcpy(shaderData.buffer.mapped, &shaderData.values, sizeof(shaderData.values));
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanExample::prepare()
|
||||||
|
{
|
||||||
|
VulkanExampleBase::prepare();
|
||||||
|
loadAssets();
|
||||||
|
|
||||||
|
// [POI]
|
||||||
|
vkCmdBindShadingRateImageNV = reinterpret_cast<PFN_vkCmdBindShadingRateImageNV>(vkGetDeviceProcAddr(device, "vkCmdBindShadingRateImageNV"));
|
||||||
|
physicalDeviceShadingRateImagePropertiesNV.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADING_RATE_IMAGE_PROPERTIES_NV;
|
||||||
|
VkPhysicalDeviceProperties2 deviceProperties2{};
|
||||||
|
deviceProperties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
|
||||||
|
deviceProperties2.pNext = &physicalDeviceShadingRateImagePropertiesNV;
|
||||||
|
vkGetPhysicalDeviceProperties2(physicalDevice, &deviceProperties2);
|
||||||
|
|
||||||
|
prepareShadingRateImage();
|
||||||
|
prepareUniformBuffers();
|
||||||
|
setupDescriptors();
|
||||||
|
preparePipelines();
|
||||||
|
buildCommandBuffers();
|
||||||
|
prepared = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanExample::render()
|
||||||
|
{
|
||||||
|
renderFrame();
|
||||||
|
if (camera.updated) {
|
||||||
|
updateUniformBuffers();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanExample::OnUpdateUIOverlay(vks::UIOverlay* overlay)
|
||||||
|
{
|
||||||
|
if (overlay->checkBox("Enable shading rate", &enableShadingRate)) {
|
||||||
|
buildCommandBuffers();
|
||||||
|
}
|
||||||
|
if (overlay->checkBox("Color shading rates", &colorShadingRate)) {
|
||||||
|
updateUniformBuffers();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VULKAN_EXAMPLE_MAIN()
|
||||||
71
examples/variablerateshading/variablerateshading.h
Normal file
71
examples/variablerateshading/variablerateshading.h
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
/*
|
||||||
|
* Vulkan Example - Variable rate shading
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 by Sascha Willems - www.saschawillems.de
|
||||||
|
*
|
||||||
|
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "vulkanexamplebase.h"
|
||||||
|
#include "VulkanglTFModel.h"
|
||||||
|
|
||||||
|
#define ENABLE_VALIDATION false
|
||||||
|
|
||||||
|
class VulkanExample : public VulkanExampleBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
vkglTF::Model scene;
|
||||||
|
|
||||||
|
struct ShadingRateImage {
|
||||||
|
VkImage image;
|
||||||
|
VkDeviceMemory memory;
|
||||||
|
VkImageView view;
|
||||||
|
} shadingRateImage;
|
||||||
|
|
||||||
|
bool enableShadingRate = true;
|
||||||
|
bool colorShadingRate = false;
|
||||||
|
|
||||||
|
struct ShaderData {
|
||||||
|
vks::Buffer buffer;
|
||||||
|
struct Values {
|
||||||
|
glm::mat4 projection;
|
||||||
|
glm::mat4 view;
|
||||||
|
glm::mat4 model = glm::mat4(1.0f);
|
||||||
|
glm::vec4 lightPos = glm::vec4(0.0f, 2.5f, 0.0f, 1.0f);
|
||||||
|
glm::vec4 viewPos;
|
||||||
|
int32_t colorShadingRate;
|
||||||
|
} values;
|
||||||
|
} shaderData;
|
||||||
|
|
||||||
|
struct Pipelines {
|
||||||
|
VkPipeline opaque;
|
||||||
|
VkPipeline masked;
|
||||||
|
};
|
||||||
|
|
||||||
|
Pipelines basePipelines;
|
||||||
|
Pipelines shadingRatePipelines;
|
||||||
|
|
||||||
|
VkPipelineLayout pipelineLayout;
|
||||||
|
VkDescriptorSet descriptorSet;
|
||||||
|
VkDescriptorSetLayout descriptorSetLayout;
|
||||||
|
|
||||||
|
VkPhysicalDeviceShadingRateImagePropertiesNV physicalDeviceShadingRateImagePropertiesNV{};
|
||||||
|
VkPhysicalDeviceShadingRateImageFeaturesNV enabledPhysicalDeviceShadingRateImageFeaturesNV{};
|
||||||
|
PFN_vkCmdBindShadingRateImageNV vkCmdBindShadingRateImageNV;
|
||||||
|
|
||||||
|
VulkanExample();
|
||||||
|
~VulkanExample();
|
||||||
|
virtual void getEnabledFeatures();
|
||||||
|
void handleResize();
|
||||||
|
void buildCommandBuffers();
|
||||||
|
void loadglTFFile(std::string filename);
|
||||||
|
void loadAssets();
|
||||||
|
void prepareShadingRateImage();
|
||||||
|
void setupDescriptors();
|
||||||
|
void preparePipelines();
|
||||||
|
void prepareUniformBuffers();
|
||||||
|
void updateUniformBuffers();
|
||||||
|
void prepare();
|
||||||
|
virtual void render();
|
||||||
|
virtual void OnUpdateUIOverlay(vks::UIOverlay* overlay);
|
||||||
|
};
|
||||||
BIN
screenshots/variablerateshading.jpg
Normal file
BIN
screenshots/variablerateshading.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 156 KiB |
Loading…
Add table
Add a link
Reference in a new issue