Merge branch 'variable_rate_shading'

This commit is contained in:
Sascha Willems 2020-09-13 10:28:03 +02:00
commit 3cb3df4cbf
15 changed files with 953 additions and 28 deletions

View file

@ -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/)

View file

@ -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);
} }
} }

View file

@ -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;

View 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;
}
}
}

Binary file not shown.

View 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;
}

Binary file not shown.

View 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;
}

Binary file not shown.

View 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;
}

Binary file not shown.

View file

@ -122,6 +122,7 @@ set(EXAMPLES
texturemipmapgen texturemipmapgen
texturesparseresidency texturesparseresidency
triangle triangle
variablerateshading
viewportarray viewportarray
vulkanscene vulkanscene
) )

View 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()

View 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);
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB