181 lines
8.0 KiB
C++
181 lines
8.0 KiB
C++
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
|
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
#include <TestFramework.h>
|
|
|
|
#include <Renderer/VK/TextureVK.h>
|
|
#include <Renderer/VK/RendererVK.h>
|
|
#include <Renderer/VK/FatalErrorIfFailedVK.h>
|
|
#include <Image/BlitSurface.h>
|
|
|
|
TextureVK::TextureVK(RendererVK *inRenderer, const Surface *inSurface) :
|
|
Texture(inSurface->GetWidth(), inSurface->GetHeight()),
|
|
mRenderer(inRenderer)
|
|
{
|
|
ESurfaceFormat format = inSurface->GetFormat();
|
|
VkFormat vk_format = VK_FORMAT_B8G8R8A8_UNORM;
|
|
switch (format)
|
|
{
|
|
case ESurfaceFormat::A4L4: vk_format = VK_FORMAT_R8G8_UNORM; format = ESurfaceFormat::A8L8; break;
|
|
case ESurfaceFormat::L8: vk_format = VK_FORMAT_R8_UNORM; break;
|
|
case ESurfaceFormat::A8: vk_format = VK_FORMAT_A8_UNORM_KHR; break;
|
|
case ESurfaceFormat::A8L8: vk_format = VK_FORMAT_R8G8_UNORM; break;
|
|
case ESurfaceFormat::R5G6B5: vk_format = VK_FORMAT_B5G6R5_UNORM_PACK16; break;
|
|
case ESurfaceFormat::X1R5G5B5: vk_format = VK_FORMAT_B5G5R5A1_UNORM_PACK16; format = ESurfaceFormat::A1R5G5B5; break;
|
|
case ESurfaceFormat::X4R4G4B4: vk_format = VK_FORMAT_B4G4R4A4_UNORM_PACK16; format = ESurfaceFormat::A4R4G4B4; break;
|
|
case ESurfaceFormat::A1R5G5B5: vk_format = VK_FORMAT_B5G5R5A1_UNORM_PACK16; break;
|
|
case ESurfaceFormat::A4R4G4B4: vk_format = VK_FORMAT_B4G4R4A4_UNORM_PACK16; break;
|
|
case ESurfaceFormat::R8G8B8: vk_format = VK_FORMAT_B8G8R8_UNORM; break;
|
|
case ESurfaceFormat::B8G8R8: vk_format = VK_FORMAT_B8G8R8_UNORM; break;
|
|
case ESurfaceFormat::X8R8G8B8: vk_format = VK_FORMAT_B8G8R8A8_UNORM; format = ESurfaceFormat::A8R8G8B8; break;
|
|
case ESurfaceFormat::X8B8G8R8: vk_format = VK_FORMAT_B8G8R8A8_UNORM; format = ESurfaceFormat::X8R8G8B8; break;
|
|
case ESurfaceFormat::A8R8G8B8: vk_format = VK_FORMAT_B8G8R8A8_UNORM; break;
|
|
case ESurfaceFormat::A8B8G8R8: vk_format = VK_FORMAT_B8G8R8A8_UNORM; format = ESurfaceFormat::A8R8G8B8; break;
|
|
case ESurfaceFormat::Invalid:
|
|
default: JPH_ASSERT(false); break;
|
|
}
|
|
|
|
// Blit the surface to another temporary surface if the format changed
|
|
const Surface *surface = inSurface;
|
|
Ref<Surface> tmp;
|
|
if (format != inSurface->GetFormat())
|
|
{
|
|
tmp = new SoftwareSurface(mWidth, mHeight, format);
|
|
BlitSurface(inSurface, tmp);
|
|
surface = tmp;
|
|
}
|
|
|
|
int bpp = surface->GetBytesPerPixel();
|
|
VkDeviceSize image_size = VkDeviceSize(mWidth) * mHeight * bpp;
|
|
VkDevice device = mRenderer->GetDevice();
|
|
|
|
BufferVK staging_buffer;
|
|
mRenderer->CreateBuffer(image_size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, staging_buffer);
|
|
|
|
// Copy data to upload texture
|
|
surface->Lock(ESurfaceLockMode::Read);
|
|
void *data;
|
|
vkMapMemory(device, staging_buffer.mMemory, staging_buffer.mOffset, image_size, 0, &data);
|
|
for (int y = 0; y < mHeight; ++y)
|
|
memcpy(reinterpret_cast<uint8 *>(data) + y * mWidth * bpp, surface->GetData() + y * surface->GetStride(), mWidth * bpp);
|
|
vkUnmapMemory(device, staging_buffer.mMemory);
|
|
surface->UnLock();
|
|
|
|
// Create destination image
|
|
mRenderer->CreateImage(mWidth, mHeight, vk_format, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, mImage, mImageMemory);
|
|
|
|
VkCommandBuffer command_buffer = mRenderer->StartTempCommandBuffer();
|
|
|
|
// Make the image suitable for transferring to
|
|
TransitionImageLayout(command_buffer, mImage, vk_format, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
|
|
|
// Copy the data to the destination image
|
|
VkBufferImageCopy region = {};
|
|
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
region.imageSubresource.layerCount = 1;
|
|
region.imageExtent = { uint32(mWidth), uint32(mHeight), 1 };
|
|
vkCmdCopyBufferToImage(command_buffer, staging_buffer.mBuffer, mImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion);
|
|
|
|
// Make the image suitable for sampling
|
|
TransitionImageLayout(command_buffer, mImage, vk_format, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
|
|
|
mRenderer->EndTempCommandBuffer(command_buffer);
|
|
|
|
// Destroy temporary buffer
|
|
mRenderer->FreeBuffer(staging_buffer);
|
|
|
|
CreateImageViewAndDescriptorSet(vk_format, VK_IMAGE_ASPECT_COLOR_BIT, mRenderer->GetTextureSamplerRepeat());
|
|
}
|
|
|
|
TextureVK::TextureVK(RendererVK *inRenderer, int inWidth, int inHeight) :
|
|
Texture(inWidth, inHeight),
|
|
mRenderer(inRenderer)
|
|
{
|
|
VkFormat vk_format = mRenderer->FindDepthFormat();
|
|
|
|
// Create render target
|
|
mRenderer->CreateImage(mWidth, mHeight, vk_format, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, mImage, mImageMemory);
|
|
|
|
CreateImageViewAndDescriptorSet(vk_format, VK_IMAGE_ASPECT_DEPTH_BIT, mRenderer->GetTextureSamplerShadow());
|
|
}
|
|
|
|
TextureVK::~TextureVK()
|
|
{
|
|
if (mImage != VK_NULL_HANDLE)
|
|
{
|
|
VkDevice device = mRenderer->GetDevice();
|
|
|
|
vkDeviceWaitIdle(device);
|
|
|
|
vkDestroyImageView(device, mImageView, nullptr);
|
|
|
|
mRenderer->DestroyImage(mImage, mImageMemory);
|
|
}
|
|
}
|
|
|
|
void TextureVK::Bind() const
|
|
{
|
|
if (mDescriptorSet != VK_NULL_HANDLE)
|
|
vkCmdBindDescriptorSets(mRenderer->GetCommandBuffer(), VK_PIPELINE_BIND_POINT_GRAPHICS, mRenderer->GetPipelineLayout(), 1, 1, &mDescriptorSet, 0, nullptr);
|
|
}
|
|
|
|
void TextureVK::CreateImageViewAndDescriptorSet(VkFormat inFormat, VkImageAspectFlags inAspectFlags, VkSampler inSampler)
|
|
{
|
|
VkDevice device = mRenderer->GetDevice();
|
|
|
|
// Create image view
|
|
mImageView = mRenderer->CreateImageView(mImage, inFormat, inAspectFlags);
|
|
|
|
// Allocate descriptor set for binding the texture
|
|
VkDescriptorSetLayout layout = mRenderer->GetDescriptorSetLayoutTexture();
|
|
VkDescriptorSetAllocateInfo descriptor_set_alloc_info = {};
|
|
descriptor_set_alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
|
descriptor_set_alloc_info.descriptorPool = mRenderer->GetDescriptorPool();
|
|
descriptor_set_alloc_info.descriptorSetCount = 1;
|
|
descriptor_set_alloc_info.pSetLayouts = &layout;
|
|
FatalErrorIfFailed(vkAllocateDescriptorSets(device, &descriptor_set_alloc_info, &mDescriptorSet));
|
|
|
|
VkDescriptorImageInfo image_info = {};
|
|
image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
|
image_info.imageView = mImageView;
|
|
image_info.sampler = inSampler;
|
|
|
|
VkWriteDescriptorSet descriptor_write = {};
|
|
descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
descriptor_write.dstSet = mDescriptorSet;
|
|
descriptor_write.dstBinding = 0;
|
|
descriptor_write.dstArrayElement = 0;
|
|
descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
|
descriptor_write.descriptorCount = 1;
|
|
descriptor_write.pImageInfo = &image_info;
|
|
vkUpdateDescriptorSets(device, 1, &descriptor_write, 0, nullptr);
|
|
}
|
|
|
|
void TextureVK::TransitionImageLayout(VkCommandBuffer inCommandBuffer, VkImage inImage, VkFormat inFormat, VkImageLayout inOldLayout, VkImageLayout inNewLayout)
|
|
{
|
|
VkImageMemoryBarrier barrier = {};
|
|
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
|
barrier.oldLayout = inOldLayout;
|
|
barrier.newLayout = inNewLayout;
|
|
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
barrier.image = inImage;
|
|
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
barrier.subresourceRange.levelCount = 1;
|
|
barrier.subresourceRange.layerCount = 1;
|
|
|
|
if (inOldLayout == VK_IMAGE_LAYOUT_UNDEFINED && inNewLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
|
|
{
|
|
barrier.srcAccessMask = 0;
|
|
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
|
vkCmdPipelineBarrier(inCommandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
|
|
}
|
|
else if (inOldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && inNewLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
|
|
{
|
|
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
|
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
|
vkCmdPipelineBarrier(inCommandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
|
|
}
|
|
}
|