Add Image, and misc. changes
This commit is contained in:
parent
9738de012a
commit
47a2ea4ebf
@ -1,11 +1,11 @@
|
|||||||
# NF library CMakeLists.txt
|
# NF library CMakeLists.txt
|
||||||
add_library(NothinFancy STATIC "src/Engine.cpp" "src/include/nf.h" "src/pch.h" "src/util.h" "src/util/log.h" "src/util/log.cpp" "src/include/nf/config.h" "src/util/util.cpp" "src/util/file.h" "src/util/file.cpp" "src/client/Client.h" "src/client/Client.cpp" "src/client/Window.h" "src/client/Window.cpp" "src/client/render/RenderEngine.h" "src/client/render/RenderEngine.cpp" "src/client/render/ShaderModule.h" "src/client/render/ShaderModule.cpp" "src/client/render/Resource.h" "src/client/render/Buffer.h" "src/client/render/Buffer.cpp" "src/client/render/VideoMemoryAllocator.h" "src/client/render/VideoMemoryAllocator.cpp")
|
add_library(NothinFancy STATIC "src/Engine.cpp" "src/include/nf.h" "src/pch.h" "src/util.h" "src/util/log.h" "src/util/log.cpp" "src/include/nf/config.h" "src/util/util.cpp" "src/util/file.h" "src/util/file.cpp" "src/client/Client.h" "src/client/Client.cpp" "src/client/Window.h" "src/client/Window.cpp" "src/client/render/RenderEngine.h" "src/client/render/RenderEngine.cpp" "src/client/render/ShaderModule.h" "src/client/render/ShaderModule.cpp" "src/client/render/GraphicsResource.h" "src/client/render/Buffer.h" "src/client/render/Buffer.cpp" "src/client/render/VideoMemoryAllocator.h" "src/client/render/VideoMemoryAllocator.cpp" "src/client/render/Image.h" "src/client/render/Image.cpp")
|
||||||
|
|
||||||
# Use C++20
|
# Use C++20
|
||||||
set_property(TARGET NothinFancy PROPERTY CXX_STANDARD 20)
|
set_property(TARGET NothinFancy PROPERTY CXX_STANDARD 20)
|
||||||
|
|
||||||
# Additional include directories
|
# Additional include directories
|
||||||
target_include_directories(NothinFancy PUBLIC "src" "src/include")
|
target_include_directories(NothinFancy PUBLIC "src" "src/include" "dep/include")
|
||||||
|
|
||||||
# Use precompiled header
|
# Use precompiled header
|
||||||
target_precompile_headers(NothinFancy PUBLIC "src/pch.h")
|
target_precompile_headers(NothinFancy PUBLIC "src/pch.h")
|
||||||
|
7988
NothinFancy/dep/include/stb_image.h
Normal file
7988
NothinFancy/dep/include/stb_image.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,9 +1,14 @@
|
|||||||
#version 450
|
#version 450
|
||||||
|
|
||||||
layout(location = 0) in vec3 fragColor;
|
// Inputs
|
||||||
|
layout(location = 0) in vec2 inTextureCoordinates;
|
||||||
|
|
||||||
|
// Outputs
|
||||||
layout(location = 0) out vec4 outColor;
|
layout(location = 0) out vec4 outColor;
|
||||||
|
|
||||||
|
// Uniforms
|
||||||
|
layout(binding = 1) uniform sampler2D image;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
outColor = vec4(fragColor, 1.0);
|
outColor = texture(image, inTextureCoordinates);
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
#version 450
|
#version 450
|
||||||
|
|
||||||
// Inputs
|
// Inputs
|
||||||
layout(location = 0) in vec2 inPosition;
|
layout(location = 0) in vec3 inPosition;
|
||||||
layout(location = 1) in vec3 inColor;
|
layout(location = 1) in vec2 inTextureCoordinates;
|
||||||
|
|
||||||
// Outputs
|
// Outputs
|
||||||
layout(location = 0) out vec3 fragColor;
|
layout(location = 0) out vec2 outTextureCoordinates;
|
||||||
|
|
||||||
// Uniforms
|
// Uniforms
|
||||||
layout(binding = 0) uniform MVPMatrixUniformBufferObject {
|
layout(binding = 0) uniform MVPMatrixUniformBufferObject {
|
||||||
@ -13,6 +13,6 @@ layout(binding = 0) uniform MVPMatrixUniformBufferObject {
|
|||||||
} mvpUBO;
|
} mvpUBO;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
fragColor = inColor;
|
outTextureCoordinates = inTextureCoordinates;
|
||||||
gl_Position = mvpUBO.mvp * vec4(inPosition, 0.0, 1.0);
|
gl_Position = mvpUBO.mvp * vec4(inPosition, 1.0);
|
||||||
}
|
}
|
||||||
|
@ -6,34 +6,29 @@
|
|||||||
|
|
||||||
namespace nf::client::render {
|
namespace nf::client::render {
|
||||||
Buffer::Buffer(BufferType type, const VkDevice& device, VideoMemoryAllocator& allocator, const VkCommandPool& commandPool, const VkQueue& queue, void* bufferData, size_t bufferSize)
|
Buffer::Buffer(BufferType type, const VkDevice& device, VideoMemoryAllocator& allocator, const VkCommandPool& commandPool, const VkQueue& queue, void* bufferData, size_t bufferSize)
|
||||||
: Resource(device)
|
: GraphicsResource(device)
|
||||||
, m_allocator(allocator)
|
, m_allocator(allocator)
|
||||||
, m_handle()
|
, m_handle()
|
||||||
, m_memory()
|
, m_allocation()
|
||||||
, m_offset()
|
, m_indexCount()
|
||||||
, m_pointer()
|
|
||||||
{
|
{
|
||||||
if (type == BufferType::Uniform) {
|
if (type == BufferType::Staging) {
|
||||||
createBuffer(bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, m_handle);
|
createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, m_handle);
|
||||||
m_allocator.allocateForBuffer(m_handle, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, m_memory, m_offset);
|
m_allocator.allocateForBuffer(m_handle, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, m_allocation);
|
||||||
vkBindBufferMemory(m_device, m_handle, m_memory, m_offset);
|
if (m_allocation.mappedMemoryPointer)
|
||||||
|
memcpy(m_allocation.mappedMemoryPointer, bufferData, bufferSize);
|
||||||
|
|
||||||
vkMapMemory(m_device, m_memory, m_offset, bufferSize, 0, &m_pointer);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
VkBuffer stagingBuffer = nullptr;
|
if (type == BufferType::Uniform) {
|
||||||
createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, stagingBuffer);
|
createBuffer(bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, m_handle);
|
||||||
|
m_allocator.allocateForBuffer(m_handle, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, m_allocation);
|
||||||
|
|
||||||
VkDeviceMemory stagingBufferMemory = nullptr;
|
return;
|
||||||
VkDeviceSize stagingBufferMemoryOffset = 0;
|
}
|
||||||
m_allocator.allocateForBuffer(stagingBuffer, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBufferMemory, stagingBufferMemoryOffset);
|
|
||||||
vkBindBufferMemory(m_device, stagingBuffer, stagingBufferMemory, stagingBufferMemoryOffset);
|
|
||||||
|
|
||||||
void* stagingBufferPointer = nullptr;
|
Buffer stagingBuffer(BufferType::Staging, m_device, m_allocator, nullptr, nullptr, bufferData, bufferSize);
|
||||||
vkMapMemory(m_device, stagingBufferMemory, stagingBufferMemoryOffset, bufferSize, 0, &stagingBufferPointer);
|
|
||||||
std::memcpy(stagingBufferPointer, bufferData, bufferSize);
|
|
||||||
vkUnmapMemory(m_device, stagingBufferMemory);
|
|
||||||
|
|
||||||
VkBufferUsageFlags mainUsage = NULL;
|
VkBufferUsageFlags mainUsage = NULL;
|
||||||
|
|
||||||
@ -44,16 +39,14 @@ namespace nf::client::render {
|
|||||||
|
|
||||||
case BufferType::Index:
|
case BufferType::Index:
|
||||||
mainUsage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
|
mainUsage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
|
||||||
|
m_indexCount = bufferSize / sizeof(uint32_t);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | mainUsage, m_handle);
|
createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | mainUsage, m_handle);
|
||||||
m_allocator.allocateForBuffer(m_handle, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, m_memory, m_offset);
|
m_allocator.allocateForBuffer(m_handle, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, m_allocation);
|
||||||
vkBindBufferMemory(m_device, m_handle, m_memory, m_offset);
|
|
||||||
|
|
||||||
copyBuffer(stagingBuffer, m_handle, bufferSize, commandPool, queue);
|
copyBuffer(stagingBuffer.getHandle(), m_handle, bufferSize, commandPool, queue);
|
||||||
|
|
||||||
destroyBuffer(stagingBuffer, stagingBufferMemory, stagingBufferMemoryOffset);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const VkBuffer& Buffer::getHandle() const {
|
const VkBuffer& Buffer::getHandle() const {
|
||||||
@ -61,7 +54,11 @@ namespace nf::client::render {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void* Buffer::getPointer() const {
|
void* Buffer::getPointer() const {
|
||||||
return m_pointer;
|
return m_allocation.mappedMemoryPointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Buffer::getIndicesCount() const {
|
||||||
|
return m_indexCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Buffer::createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkBuffer& buffer) {
|
void Buffer::createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkBuffer& buffer) {
|
||||||
@ -85,8 +82,7 @@ namespace nf::client::render {
|
|||||||
VkCommandBufferBeginInfo commandBufferBI = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
|
VkCommandBufferBeginInfo commandBufferBI = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
|
||||||
commandBufferBI.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
|
commandBufferBI.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
|
||||||
|
|
||||||
if (vkBeginCommandBuffer(commandBuffer, &commandBufferBI) != VK_SUCCESS)
|
vkBeginCommandBuffer(commandBuffer, &commandBufferBI);
|
||||||
NFError("Could not begin recording command buffer.");
|
|
||||||
|
|
||||||
VkBufferCopy bufferCopyRegion = {};
|
VkBufferCopy bufferCopyRegion = {};
|
||||||
bufferCopyRegion.size = size;
|
bufferCopyRegion.size = size;
|
||||||
@ -107,12 +103,12 @@ namespace nf::client::render {
|
|||||||
vkFreeCommandBuffers(m_device, commandPool, 1, &commandBuffer);
|
vkFreeCommandBuffers(m_device, commandPool, 1, &commandBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Buffer::destroyBuffer(VkBuffer buffer, VkDeviceMemory bufferMemory, VkDeviceSize offset) {
|
void Buffer::destroyBuffer(VkBuffer buffer, VideoMemoryAllocation& allocation) {
|
||||||
m_allocator.deallocate(bufferMemory, offset);
|
m_allocator.deallocate(allocation);
|
||||||
vkDestroyBuffer(m_device, buffer, nullptr);
|
vkDestroyBuffer(m_device, buffer, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
Buffer::~Buffer() {
|
Buffer::~Buffer() {
|
||||||
destroyBuffer(m_handle, m_memory, m_offset);
|
destroyBuffer(m_handle, m_allocation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,35 +1,36 @@
|
|||||||
// Buffer class header
|
// Buffer class header
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Resource.h"
|
#include "GraphicsResource.h"
|
||||||
#include "VideoMemoryAllocator.h"
|
#include "VideoMemoryAllocator.h"
|
||||||
|
|
||||||
namespace nf::client::render {
|
namespace nf::client::render {
|
||||||
enum class BufferType {
|
enum class BufferType {
|
||||||
|
Staging,
|
||||||
Vertex,
|
Vertex,
|
||||||
Index,
|
Index,
|
||||||
Uniform
|
Uniform
|
||||||
};
|
};
|
||||||
|
|
||||||
class Buffer : Resource {
|
class Buffer : GraphicsResource {
|
||||||
public:
|
public:
|
||||||
Buffer(BufferType type, const VkDevice& device, VideoMemoryAllocator& allocator, const VkCommandPool& commandPool, const VkQueue& queue, void* bufferData, size_t bufferSize);
|
Buffer(BufferType type, const VkDevice& device, VideoMemoryAllocator& allocator, const VkCommandPool& commandPool, const VkQueue& queue, void* bufferData, size_t bufferSize);
|
||||||
|
|
||||||
const VkBuffer& getHandle() const;
|
const VkBuffer& getHandle() const;
|
||||||
void* getPointer() const;
|
void* getPointer() const;
|
||||||
|
uint32_t getIndicesCount() const;
|
||||||
|
|
||||||
~Buffer();
|
~Buffer();
|
||||||
private:
|
private:
|
||||||
void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkBuffer& buffer);
|
void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkBuffer& buffer);
|
||||||
void copyBuffer(VkBuffer sourceBuffer, VkBuffer destinationBuffer, VkDeviceSize size, VkCommandPool commandPool, VkQueue queue);
|
void copyBuffer(VkBuffer sourceBuffer, VkBuffer destinationBuffer, VkDeviceSize size, VkCommandPool commandPool, VkQueue queue);
|
||||||
|
|
||||||
void destroyBuffer(VkBuffer buffer, VkDeviceMemory bufferMemory, VkDeviceSize offset);
|
void destroyBuffer(VkBuffer buffer, VideoMemoryAllocation& allocation);
|
||||||
|
|
||||||
VideoMemoryAllocator& m_allocator;
|
VideoMemoryAllocator& m_allocator;
|
||||||
|
|
||||||
VkBuffer m_handle;
|
VkBuffer m_handle;
|
||||||
VkDeviceMemory m_memory;
|
VideoMemoryAllocation m_allocation;
|
||||||
VkDeviceSize m_offset;
|
uint32_t m_indexCount;
|
||||||
void* m_pointer;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace nf::client::render {
|
namespace nf::client::render {
|
||||||
class Resource {
|
class GraphicsResource {
|
||||||
public:
|
public:
|
||||||
Resource(const VkDevice& device)
|
GraphicsResource(const VkDevice& device)
|
||||||
: m_device(device)
|
: m_device(device)
|
||||||
{}
|
{}
|
||||||
|
|
122
NothinFancy/src/client/render/Image.cpp
Normal file
122
NothinFancy/src/client/render/Image.cpp
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
// Image class implementation
|
||||||
|
#include "pch.h"
|
||||||
|
|
||||||
|
#include "Image.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "Buffer.h"
|
||||||
|
|
||||||
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
|
#include "stb_image.h"
|
||||||
|
|
||||||
|
namespace nf::client::render {
|
||||||
|
Image::Image(const VkDevice& device, VideoMemoryAllocator& allocator, const VkCommandPool& commandPool, const VkQueue& queue, const std::string& imageData)
|
||||||
|
: GraphicsResource(device)
|
||||||
|
, m_allocator(allocator)
|
||||||
|
, m_handle()
|
||||||
|
, m_allocation()
|
||||||
|
, m_view()
|
||||||
|
{
|
||||||
|
int imageWidth = 0, imageHeight = 0, numChannels = 0;
|
||||||
|
stbi_uc* rawImageData = stbi_load_from_memory(reinterpret_cast<const stbi_uc*>(imageData.data()), imageData.size(), &imageWidth, &imageHeight, &numChannels, STBI_rgb_alpha);
|
||||||
|
|
||||||
|
VkDeviceSize imageSize = imageWidth * imageHeight * 4;
|
||||||
|
|
||||||
|
Buffer stagingBuffer(BufferType::Staging, m_device, m_allocator, nullptr, nullptr, rawImageData, imageSize);
|
||||||
|
|
||||||
|
stbi_image_free(rawImageData);
|
||||||
|
|
||||||
|
VkImageCreateInfo imageCI = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
|
||||||
|
imageCI.imageType = VK_IMAGE_TYPE_2D;
|
||||||
|
imageCI.extent.width = imageWidth, imageCI.extent.height = imageHeight, imageCI.extent.depth = 1;
|
||||||
|
imageCI.mipLevels = 1;
|
||||||
|
imageCI.arrayLayers = 1;
|
||||||
|
imageCI.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||||
|
imageCI.format = VK_FORMAT_R8G8B8A8_SRGB;
|
||||||
|
imageCI.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||||
|
imageCI.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||||
|
|
||||||
|
if (vkCreateImage(m_device, &imageCI, nullptr, &m_handle) != VK_SUCCESS)
|
||||||
|
NFError("Could not create image.");
|
||||||
|
|
||||||
|
m_allocator.allocateForImage(m_handle, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, m_allocation);
|
||||||
|
|
||||||
|
VkCommandBuffer commandBuffer = nullptr;
|
||||||
|
VkCommandBufferAllocateInfo commandBufferAI = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
|
||||||
|
commandBufferAI.commandPool = commandPool;
|
||||||
|
commandBufferAI.commandBufferCount = 1;
|
||||||
|
vkAllocateCommandBuffers(m_device, &commandBufferAI, &commandBuffer);
|
||||||
|
|
||||||
|
VkCommandBufferBeginInfo commandBufferBI = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
|
||||||
|
commandBufferBI.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
|
||||||
|
|
||||||
|
vkBeginCommandBuffer(commandBuffer, &commandBufferBI);
|
||||||
|
|
||||||
|
// Image transition undefined -> transfer destination
|
||||||
|
VkImageMemoryBarrier imageMemoryBarrier = { {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER} };
|
||||||
|
imageMemoryBarrier.image = m_handle;
|
||||||
|
imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||||
|
imageMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||||
|
imageMemoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||||
|
imageMemoryBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
|
imageMemoryBarrier.subresourceRange.levelCount = 1;
|
||||||
|
imageMemoryBarrier.subresourceRange.layerCount = 1;
|
||||||
|
imageMemoryBarrier.srcAccessMask = 0;
|
||||||
|
imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||||
|
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier);
|
||||||
|
vkEndCommandBuffer(commandBuffer);
|
||||||
|
|
||||||
|
VkSubmitInfo submitInfo = { VK_STRUCTURE_TYPE_SUBMIT_INFO };
|
||||||
|
submitInfo.commandBufferCount = 1;
|
||||||
|
submitInfo.pCommandBuffers = &commandBuffer;
|
||||||
|
vkQueueSubmit(queue, 1, &submitInfo, nullptr);
|
||||||
|
vkQueueWaitIdle(queue);
|
||||||
|
|
||||||
|
vkBeginCommandBuffer(commandBuffer, &commandBufferBI);
|
||||||
|
|
||||||
|
VkBufferImageCopy bufferImageCopyRegion = {};
|
||||||
|
bufferImageCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
|
bufferImageCopyRegion.imageSubresource.layerCount = 1;
|
||||||
|
bufferImageCopyRegion.imageExtent = { static_cast<uint32_t>(imageWidth), static_cast<uint32_t>(imageHeight), 1 };
|
||||||
|
vkCmdCopyBufferToImage(commandBuffer, stagingBuffer.getHandle(), m_handle, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &bufferImageCopyRegion);
|
||||||
|
|
||||||
|
vkEndCommandBuffer(commandBuffer);
|
||||||
|
vkQueueSubmit(queue, 1, &submitInfo, nullptr);
|
||||||
|
vkQueueWaitIdle(queue);
|
||||||
|
|
||||||
|
vkBeginCommandBuffer(commandBuffer, &commandBufferBI);
|
||||||
|
|
||||||
|
// Image transition transfer destination -> shader read only
|
||||||
|
imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||||
|
imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||||
|
imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||||
|
imageMemoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
||||||
|
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier);
|
||||||
|
vkEndCommandBuffer(commandBuffer);
|
||||||
|
vkQueueSubmit(queue, 1, &submitInfo, nullptr);
|
||||||
|
vkQueueWaitIdle(queue);
|
||||||
|
|
||||||
|
vkFreeCommandBuffers(m_device, commandPool, 1, &commandBuffer);
|
||||||
|
|
||||||
|
// Create image view
|
||||||
|
VkImageViewCreateInfo imageViewCI = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
|
||||||
|
imageViewCI.image = m_handle;
|
||||||
|
imageViewCI.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||||
|
imageViewCI.format = VK_FORMAT_R8G8B8A8_SRGB;
|
||||||
|
imageViewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
|
imageViewCI.subresourceRange.levelCount = 1;
|
||||||
|
imageViewCI.subresourceRange.layerCount = 1;
|
||||||
|
|
||||||
|
if (vkCreateImageView(m_device, &imageViewCI, nullptr, &m_view) != VK_SUCCESS)
|
||||||
|
NFError("Could not create image view.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const VkImageView& Image::getView() const {
|
||||||
|
return m_view;
|
||||||
|
}
|
||||||
|
|
||||||
|
Image::~Image() {
|
||||||
|
vkDestroyImageView(m_device, m_view, nullptr);
|
||||||
|
m_allocator.deallocate(m_allocation);
|
||||||
|
vkDestroyImage(m_device, m_handle, nullptr);
|
||||||
|
}
|
||||||
|
}
|
22
NothinFancy/src/client/render/Image.h
Normal file
22
NothinFancy/src/client/render/Image.h
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// Image class header
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "GraphicsResource.h"
|
||||||
|
#include "VideoMemoryAllocator.h"
|
||||||
|
|
||||||
|
namespace nf::client::render {
|
||||||
|
class Image : GraphicsResource {
|
||||||
|
public:
|
||||||
|
Image(const VkDevice& device, VideoMemoryAllocator& allocator, const VkCommandPool& commandPool, const VkQueue& queue, const std::string& data);
|
||||||
|
|
||||||
|
const VkImageView& getView() const;
|
||||||
|
|
||||||
|
~Image();
|
||||||
|
private:
|
||||||
|
VideoMemoryAllocator& m_allocator;
|
||||||
|
|
||||||
|
VkImage m_handle;
|
||||||
|
VideoMemoryAllocation m_allocation;
|
||||||
|
VkImageView m_view;
|
||||||
|
};
|
||||||
|
}
|
@ -8,8 +8,8 @@
|
|||||||
namespace nf::client::render {
|
namespace nf::client::render {
|
||||||
// Vertex layout definition
|
// Vertex layout definition
|
||||||
struct Vertex {
|
struct Vertex {
|
||||||
glm::vec2 position;
|
glm::vec3 position;
|
||||||
glm::vec3 color;
|
glm::vec2 textureCoordinates;
|
||||||
};
|
};
|
||||||
|
|
||||||
RenderEngine::RenderEngine(std::shared_ptr<Window> window, DisplayConfig display)
|
RenderEngine::RenderEngine(std::shared_ptr<Window> window, DisplayConfig display)
|
||||||
@ -33,7 +33,6 @@ namespace nf::client::render {
|
|||||||
, m_pipelineOutputDescriptorSetLayout()
|
, m_pipelineOutputDescriptorSetLayout()
|
||||||
, m_pipelineOutputLayout()
|
, m_pipelineOutputLayout()
|
||||||
, m_pipelineOutputDescriptorPool()
|
, m_pipelineOutputDescriptorPool()
|
||||||
, m_pipelineOutputDescriptorSet()
|
|
||||||
, m_pipelineOutput()
|
, m_pipelineOutput()
|
||||||
, m_commandPool()
|
, m_commandPool()
|
||||||
, m_commandBuffer()
|
, m_commandBuffer()
|
||||||
@ -43,6 +42,10 @@ namespace nf::client::render {
|
|||||||
, m_allocator()
|
, m_allocator()
|
||||||
, m_bufferVertex()
|
, m_bufferVertex()
|
||||||
, m_bufferIndex()
|
, m_bufferIndex()
|
||||||
|
, m_imageTest()
|
||||||
|
, m_sampler()
|
||||||
|
, m_pipelineOutputDescriptorSet()
|
||||||
|
|
||||||
{
|
{
|
||||||
NFLog("Initializing render engine");
|
NFLog("Initializing render engine");
|
||||||
m_window->setDisplay(m_display);
|
m_window->setDisplay(m_display);
|
||||||
@ -59,6 +62,8 @@ namespace nf::client::render {
|
|||||||
|
|
||||||
m_allocator = std::make_unique<VideoMemoryAllocator>(m_device, m_physicalDevice);
|
m_allocator = std::make_unique<VideoMemoryAllocator>(m_device, m_physicalDevice);
|
||||||
createBuffers();
|
createBuffers();
|
||||||
|
createImage();
|
||||||
|
createDescriptorSet();
|
||||||
|
|
||||||
m_window->show();
|
m_window->show();
|
||||||
}
|
}
|
||||||
@ -104,6 +109,7 @@ namespace nf::client::render {
|
|||||||
// Then gather information on them and save first dedicated
|
// Then gather information on them and save first dedicated
|
||||||
std::optional<int> firstDedicatedPhysicalDeviceIndex;
|
std::optional<int> firstDedicatedPhysicalDeviceIndex;
|
||||||
std::vector<VkPhysicalDeviceProperties> physicalDeviceProperties;
|
std::vector<VkPhysicalDeviceProperties> physicalDeviceProperties;
|
||||||
|
physicalDeviceProperties.reserve(numPhysicalDevices);
|
||||||
for (int i = 0; i < numPhysicalDevices; i++) {
|
for (int i = 0; i < numPhysicalDevices; i++) {
|
||||||
VkPhysicalDeviceProperties currentPhysicalDeviceProperties = {};
|
VkPhysicalDeviceProperties currentPhysicalDeviceProperties = {};
|
||||||
vkGetPhysicalDeviceProperties(physicalDevices[i], ¤tPhysicalDeviceProperties);
|
vkGetPhysicalDeviceProperties(physicalDevices[i], ¤tPhysicalDeviceProperties);
|
||||||
@ -172,6 +178,7 @@ namespace nf::client::render {
|
|||||||
|
|
||||||
float queuePriority = 1.0f;
|
float queuePriority = 1.0f;
|
||||||
std::vector<VkDeviceQueueCreateInfo> queueCIs;
|
std::vector<VkDeviceQueueCreateInfo> queueCIs;
|
||||||
|
queueCIs.reserve(uniqueQueueFamilyIndices.size());
|
||||||
for (uint32_t currentQueueFamilyIndex : uniqueQueueFamilyIndices) {
|
for (uint32_t currentQueueFamilyIndex : uniqueQueueFamilyIndices) {
|
||||||
VkDeviceQueueCreateInfo queueCI = { VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO };
|
VkDeviceQueueCreateInfo queueCI = { VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO };
|
||||||
queueCI.queueFamilyIndex = currentQueueFamilyIndex;
|
queueCI.queueFamilyIndex = currentQueueFamilyIndex;
|
||||||
@ -360,11 +367,11 @@ namespace nf::client::render {
|
|||||||
vertexBindingDescription.stride = sizeof(Vertex);
|
vertexBindingDescription.stride = sizeof(Vertex);
|
||||||
std::array<VkVertexInputAttributeDescription, 2> vertexAttributeDescriptions = {};
|
std::array<VkVertexInputAttributeDescription, 2> vertexAttributeDescriptions = {};
|
||||||
vertexAttributeDescriptions[0].location = 0;
|
vertexAttributeDescriptions[0].location = 0;
|
||||||
vertexAttributeDescriptions[0].format = VK_FORMAT_R32G32_SFLOAT;
|
vertexAttributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT;
|
||||||
vertexAttributeDescriptions[0].offset = offsetof(Vertex, position);
|
vertexAttributeDescriptions[0].offset = offsetof(Vertex, position);
|
||||||
vertexAttributeDescriptions[1].location = 1;
|
vertexAttributeDescriptions[1].location = 1;
|
||||||
vertexAttributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT;
|
vertexAttributeDescriptions[1].format = VK_FORMAT_R32G32_SFLOAT;
|
||||||
vertexAttributeDescriptions[1].offset = offsetof(Vertex, color);
|
vertexAttributeDescriptions[1].offset = offsetof(Vertex, textureCoordinates);
|
||||||
vertexInputStateCI.vertexBindingDescriptionCount = 1;
|
vertexInputStateCI.vertexBindingDescriptionCount = 1;
|
||||||
vertexInputStateCI.pVertexBindingDescriptions = &vertexBindingDescription;
|
vertexInputStateCI.pVertexBindingDescriptions = &vertexBindingDescription;
|
||||||
vertexInputStateCI.vertexAttributeDescriptionCount = vertexAttributeDescriptions.size();
|
vertexInputStateCI.vertexAttributeDescriptionCount = vertexAttributeDescriptions.size();
|
||||||
@ -398,13 +405,18 @@ namespace nf::client::render {
|
|||||||
|
|
||||||
// Create descriptor set layout
|
// Create descriptor set layout
|
||||||
VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCI = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };
|
VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCI = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };
|
||||||
VkDescriptorSetLayoutBinding mvpLayoutBinding = {};
|
VkDescriptorSetLayoutBinding layoutBindings[] = { {}, {} };
|
||||||
mvpLayoutBinding.binding = 0;
|
layoutBindings[0].binding = 0;
|
||||||
mvpLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
layoutBindings[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
||||||
mvpLayoutBinding.descriptorCount = 1;
|
layoutBindings[0].descriptorCount = 1;
|
||||||
mvpLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
|
layoutBindings[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
|
||||||
descriptorSetLayoutCI.bindingCount = 1;
|
layoutBindings[1].binding = 1;
|
||||||
descriptorSetLayoutCI.pBindings = &mvpLayoutBinding;
|
layoutBindings[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||||
|
layoutBindings[1].descriptorCount = 1;
|
||||||
|
layoutBindings[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||||
|
descriptorSetLayoutCI.bindingCount = 2;
|
||||||
|
descriptorSetLayoutCI.bindingCount = 2;
|
||||||
|
descriptorSetLayoutCI.pBindings = layoutBindings;
|
||||||
|
|
||||||
if (vkCreateDescriptorSetLayout(m_device, &descriptorSetLayoutCI, nullptr, &m_pipelineOutputDescriptorSetLayout) != VK_SUCCESS)
|
if (vkCreateDescriptorSetLayout(m_device, &descriptorSetLayoutCI, nullptr, &m_pipelineOutputDescriptorSetLayout) != VK_SUCCESS)
|
||||||
NFError("Could not create descriptor set layout.");
|
NFError("Could not create descriptor set layout.");
|
||||||
@ -419,25 +431,18 @@ namespace nf::client::render {
|
|||||||
|
|
||||||
// Create descriptor pool
|
// Create descriptor pool
|
||||||
VkDescriptorPoolCreateInfo descriptorPoolCI = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO };
|
VkDescriptorPoolCreateInfo descriptorPoolCI = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO };
|
||||||
VkDescriptorPoolSize descriptorPoolSize = {};
|
VkDescriptorPoolSize descriptorPoolSizes[] = { {}, {} };
|
||||||
descriptorPoolSize.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
descriptorPoolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
||||||
descriptorPoolSize.descriptorCount = 1;
|
descriptorPoolSizes[0].descriptorCount = 1;
|
||||||
descriptorPoolCI.poolSizeCount = 1;
|
descriptorPoolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||||
descriptorPoolCI.pPoolSizes = &descriptorPoolSize;
|
descriptorPoolSizes[1].descriptorCount = 1;
|
||||||
|
descriptorPoolCI.poolSizeCount = 2;
|
||||||
|
descriptorPoolCI.pPoolSizes = descriptorPoolSizes;
|
||||||
descriptorPoolCI.maxSets = 1;
|
descriptorPoolCI.maxSets = 1;
|
||||||
|
|
||||||
if (vkCreateDescriptorPool(m_device, &descriptorPoolCI, nullptr, &m_pipelineOutputDescriptorPool) != VK_SUCCESS)
|
if (vkCreateDescriptorPool(m_device, &descriptorPoolCI, nullptr, &m_pipelineOutputDescriptorPool) != VK_SUCCESS)
|
||||||
NFError("Could not create descriptor pool.");
|
NFError("Could not create descriptor pool.");
|
||||||
|
|
||||||
// Allocate descriptor set
|
|
||||||
VkDescriptorSetAllocateInfo descriptorSetAI = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO };
|
|
||||||
descriptorSetAI.descriptorPool = m_pipelineOutputDescriptorPool;
|
|
||||||
descriptorSetAI.descriptorSetCount = 1;
|
|
||||||
descriptorSetAI.pSetLayouts = &m_pipelineOutputDescriptorSetLayout;
|
|
||||||
|
|
||||||
if (vkAllocateDescriptorSets(m_device, &descriptorSetAI, &m_pipelineOutputDescriptorSet) != VK_SUCCESS)
|
|
||||||
NFError("Could not allocate descriptor set.");
|
|
||||||
|
|
||||||
// And finally put it all together
|
// And finally put it all together
|
||||||
VkGraphicsPipelineCreateInfo pipelineCI = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO };
|
VkGraphicsPipelineCreateInfo pipelineCI = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO };
|
||||||
pipelineCI.stageCount = 2;
|
pipelineCI.stageCount = 2;
|
||||||
@ -483,37 +488,110 @@ namespace nf::client::render {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void RenderEngine::createBuffers() {
|
void RenderEngine::createBuffers() {
|
||||||
std::vector<Vertex> triangleVertices = {
|
std::vector<Vertex> cubeVertices = {
|
||||||
{{-0.5, -0.5}, {1.0, 0.0, 0.0}},
|
{{-0.5, -0.5, 0.5}, {0.0, 1.0}},
|
||||||
{{ 0.5, -0.5}, {0.0, 1.0, 0.0}},
|
{{ 0.5, -0.5, 0.5}, {1.0, 1.0}},
|
||||||
{{ 0.5, 0.5}, {0.0, 0.0, 1.0}},
|
{{ 0.5, 0.5, 0.5}, {1.0, 0.0}},
|
||||||
{{-0.5, 0.5}, {1.0, 0.0, 1.0}}
|
{{-0.5, 0.5, 0.5}, {0.0, 0.0}},
|
||||||
|
|
||||||
|
{{ 0.5, 0.5, 0.5}, {0.0, 0.0}},
|
||||||
|
{{ 0.5, -0.5, -0.5}, {1.0, 1.0}},
|
||||||
|
{{ 0.5, 0.5, -0.5}, {1.0, 0.0}},
|
||||||
|
{{ 0.5, -0.5, 0.5}, {0.0, 1.0}},
|
||||||
|
|
||||||
|
{{-0.5, 0.5, -0.5}, {0.0, 0.0}},
|
||||||
|
{{-0.5, 0.5, 0.5}, {0.0, 1.0}},
|
||||||
|
{{ 0.5, 0.5, -0.5}, {1.0, 0.0}},
|
||||||
|
{{ 0.5, 0.5, 0.5}, {1.0, 1.0}},
|
||||||
|
|
||||||
|
{{-0.5, -0.5, -0.5}, {1.0, 0.0}},
|
||||||
|
{{-0.5, -0.5, 0.5}, {1.0, 1.0}},
|
||||||
|
{{ 0.5, -0.5, -0.5}, {0.0, 0.0}},
|
||||||
|
{{ 0.5, -0.5, 0.5}, {0.0, 1.0}},
|
||||||
|
|
||||||
|
{{-0.5, 0.5, -0.5}, {0.0, 0.0}},
|
||||||
|
{{-0.5, 0.5, 0.5}, {1.0, 0.0}},
|
||||||
|
{{-0.5, -0.5, 0.5}, {1.0, 1.0}},
|
||||||
|
{{-0.5, -0.5, -0.5}, {0.0, 1.0}},
|
||||||
|
|
||||||
|
{{ 0.5, 0.5, -0.5}, {1.0, 0.0}},
|
||||||
|
{{-0.5, 0.5, -0.5}, {0.0, 0.0}},
|
||||||
|
{{-0.5, -0.5, -0.5}, {0.0, 1.0}},
|
||||||
|
{{ 0.5, -0.5, -0.5}, {1.0, 1.0}}
|
||||||
};
|
};
|
||||||
|
|
||||||
size_t triangleVerticesSize = sizeof(triangleVertices[0]) * triangleVertices.size();
|
size_t cubeVerticesSize = sizeof(cubeVertices[0]) * cubeVertices.size();
|
||||||
m_bufferVertex = std::make_unique<Buffer>(BufferType::Vertex, m_device, *m_allocator, m_commandPool, m_queueGraphics, triangleVertices.data(), triangleVerticesSize);
|
m_bufferVertex = std::make_unique<Buffer>(BufferType::Vertex, m_device, *m_allocator, m_commandPool, m_queueGraphics, cubeVertices.data(), cubeVerticesSize);
|
||||||
|
|
||||||
std::vector<uint32_t> triangleIndices = {
|
std::vector<uint32_t> cubeIndices = {
|
||||||
0, 1, 2, 2, 3, 0
|
0, 1, 2,
|
||||||
|
2, 3, 0,
|
||||||
|
|
||||||
|
4, 5, 6,
|
||||||
|
4, 7, 5,
|
||||||
|
|
||||||
|
8, 9, 10,
|
||||||
|
10, 9, 11,
|
||||||
|
|
||||||
|
12, 14, 13,
|
||||||
|
14, 15, 13,
|
||||||
|
|
||||||
|
16, 19, 17,
|
||||||
|
17, 19, 18,
|
||||||
|
|
||||||
|
20, 23, 21,
|
||||||
|
23, 22, 21
|
||||||
};
|
};
|
||||||
|
|
||||||
size_t triangleIndicesSize = sizeof(triangleIndices[0]) * triangleIndices.size();
|
size_t cubeIndicesSize = sizeof(cubeIndices[0]) * cubeIndices.size();
|
||||||
m_bufferIndex = std::make_unique<Buffer>(BufferType::Index, m_device, *m_allocator, m_commandPool, m_queueGraphics, triangleIndices.data(), triangleIndicesSize);
|
m_bufferIndex = std::make_unique<Buffer>(BufferType::Index, m_device, *m_allocator, m_commandPool, m_queueGraphics, cubeIndices.data(), cubeIndicesSize);
|
||||||
|
|
||||||
size_t mvpUniformBufferSize = sizeof(glm::mat4);
|
size_t mvpUniformBufferSize = sizeof(glm::mat4);
|
||||||
m_bufferUniformMVP = std::make_unique<Buffer>(BufferType::Uniform, m_device, *m_allocator, nullptr, nullptr, nullptr, mvpUniformBufferSize);
|
m_bufferUniformMVP = std::make_unique<Buffer>(BufferType::Uniform, m_device, *m_allocator, nullptr, nullptr, nullptr, mvpUniformBufferSize);
|
||||||
|
}
|
||||||
|
|
||||||
// Add uniform buffer to descriptor set
|
void RenderEngine::createImage() {
|
||||||
VkWriteDescriptorSet descriptorSetWrite = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET };
|
std::string imageData;
|
||||||
|
util::readFile("grayson.jpg", imageData);
|
||||||
|
m_imageTest = std::make_unique<Image>(m_device, *m_allocator, m_commandPool, m_queueGraphics, imageData);
|
||||||
|
|
||||||
|
VkSamplerCreateInfo samplerCI = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO };
|
||||||
|
samplerCI.magFilter = samplerCI.minFilter = VK_FILTER_LINEAR;
|
||||||
|
/*samplerCI.anisotropyEnable = VK_TRUE;
|
||||||
|
samplerCI.maxAnisotropy = 16;*/
|
||||||
|
|
||||||
|
if (vkCreateSampler(m_device, &samplerCI, nullptr, &m_sampler) != VK_SUCCESS)
|
||||||
|
NFError("Could not create sampler.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderEngine::createDescriptorSet() {
|
||||||
|
VkDescriptorSetAllocateInfo descriptorSetAI = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO };
|
||||||
|
descriptorSetAI.descriptorPool = m_pipelineOutputDescriptorPool;
|
||||||
|
descriptorSetAI.descriptorSetCount = 1;
|
||||||
|
descriptorSetAI.pSetLayouts = &m_pipelineOutputDescriptorSetLayout;
|
||||||
|
|
||||||
|
if (vkAllocateDescriptorSets(m_device, &descriptorSetAI, &m_pipelineOutputDescriptorSet) != VK_SUCCESS)
|
||||||
|
NFError("Could not allocate descriptor set.");
|
||||||
|
|
||||||
|
VkWriteDescriptorSet descriptorSetWrites[] = { {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET}, {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET} };
|
||||||
VkDescriptorBufferInfo uniformDescriptorBI = {};
|
VkDescriptorBufferInfo uniformDescriptorBI = {};
|
||||||
uniformDescriptorBI.buffer = m_bufferUniformMVP->getHandle();
|
uniformDescriptorBI.buffer = m_bufferUniformMVP->getHandle();
|
||||||
uniformDescriptorBI.range = VK_WHOLE_SIZE;
|
uniformDescriptorBI.range = VK_WHOLE_SIZE;
|
||||||
descriptorSetWrite.dstSet = m_pipelineOutputDescriptorSet;
|
VkDescriptorImageInfo imageDescriptorII = {};
|
||||||
descriptorSetWrite.dstBinding = 0;
|
imageDescriptorII.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||||
descriptorSetWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
imageDescriptorII.imageView = m_imageTest->getView();
|
||||||
descriptorSetWrite.descriptorCount = 1;
|
imageDescriptorII.sampler = m_sampler;
|
||||||
descriptorSetWrite.pBufferInfo = &uniformDescriptorBI;
|
descriptorSetWrites[0].dstSet = m_pipelineOutputDescriptorSet;
|
||||||
vkUpdateDescriptorSets(m_device, 1, &descriptorSetWrite, 0, nullptr);
|
descriptorSetWrites[0].dstBinding = 0;
|
||||||
|
descriptorSetWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
||||||
|
descriptorSetWrites[0].descriptorCount = 1;
|
||||||
|
descriptorSetWrites[0].pBufferInfo = &uniformDescriptorBI;
|
||||||
|
descriptorSetWrites[1].dstSet = m_pipelineOutputDescriptorSet;
|
||||||
|
descriptorSetWrites[1].dstBinding = 1;
|
||||||
|
descriptorSetWrites[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||||
|
descriptorSetWrites[1].descriptorCount = 1;
|
||||||
|
descriptorSetWrites[1].pImageInfo = &imageDescriptorII;
|
||||||
|
vkUpdateDescriptorSets(m_device, 2, descriptorSetWrites, 0, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderEngine::doFrame() {
|
void RenderEngine::doFrame() {
|
||||||
@ -536,8 +614,7 @@ namespace nf::client::render {
|
|||||||
// Begin recording
|
// Begin recording
|
||||||
VkCommandBufferBeginInfo commandBufferBI = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
|
VkCommandBufferBeginInfo commandBufferBI = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
|
||||||
|
|
||||||
if (vkBeginCommandBuffer(m_commandBuffer, &commandBufferBI) != VK_SUCCESS)
|
vkBeginCommandBuffer(m_commandBuffer, &commandBufferBI);
|
||||||
NFError("Could not begin recording command buffer.");
|
|
||||||
|
|
||||||
// Start the render pass
|
// Start the render pass
|
||||||
VkClearValue black = { {{0.0, 0.0, 0.0, 1.0}} };
|
VkClearValue black = { {{0.0, 0.0, 0.0, 1.0}} };
|
||||||
@ -576,10 +653,12 @@ namespace nf::client::render {
|
|||||||
projectionMatrix[1][1] *= -1;
|
projectionMatrix[1][1] *= -1;
|
||||||
glm::mat4 mvpMatrix = projectionMatrix * viewMatrix * modelMatrix;
|
glm::mat4 mvpMatrix = projectionMatrix * viewMatrix * modelMatrix;
|
||||||
memcpy(m_bufferUniformMVP->getPointer(), &mvpMatrix, sizeof(mvpMatrix));
|
memcpy(m_bufferUniformMVP->getPointer(), &mvpMatrix, sizeof(mvpMatrix));
|
||||||
|
|
||||||
|
// Bind descriptors
|
||||||
vkCmdBindDescriptorSets(m_commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineOutputLayout, 0, 1, &m_pipelineOutputDescriptorSet, 0, nullptr);
|
vkCmdBindDescriptorSets(m_commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineOutputLayout, 0, 1, &m_pipelineOutputDescriptorSet, 0, nullptr);
|
||||||
|
|
||||||
// Draw
|
// Draw
|
||||||
vkCmdDrawIndexed(m_commandBuffer, 6, 1, 0, 0, 0);
|
vkCmdDrawIndexed(m_commandBuffer, m_bufferIndex->getIndicesCount(), 1, 0, 0, 0);
|
||||||
|
|
||||||
// End the render pass
|
// End the render pass
|
||||||
vkCmdEndRenderPass(m_commandBuffer);
|
vkCmdEndRenderPass(m_commandBuffer);
|
||||||
@ -652,6 +731,10 @@ namespace nf::client::render {
|
|||||||
RenderEngine::~RenderEngine() {
|
RenderEngine::~RenderEngine() {
|
||||||
waitIdle();
|
waitIdle();
|
||||||
|
|
||||||
|
vkDestroySampler(m_device, m_sampler, nullptr);
|
||||||
|
|
||||||
|
m_imageTest.reset();
|
||||||
|
|
||||||
m_bufferUniformMVP.reset();
|
m_bufferUniformMVP.reset();
|
||||||
m_bufferIndex.reset();
|
m_bufferIndex.reset();
|
||||||
m_bufferVertex.reset();
|
m_bufferVertex.reset();
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include "nf/config.h"
|
#include "nf/config.h"
|
||||||
#include "VideoMemoryAllocator.h"
|
#include "VideoMemoryAllocator.h"
|
||||||
#include "Buffer.h"
|
#include "Buffer.h"
|
||||||
|
#include "Image.h"
|
||||||
|
|
||||||
namespace nf::client::render {
|
namespace nf::client::render {
|
||||||
class RenderEngine {
|
class RenderEngine {
|
||||||
@ -28,6 +29,8 @@ namespace nf::client::render {
|
|||||||
void createExecutionObjects();
|
void createExecutionObjects();
|
||||||
|
|
||||||
void createBuffers();
|
void createBuffers();
|
||||||
|
void createImage();
|
||||||
|
void createDescriptorSet();
|
||||||
|
|
||||||
void recreateSwapchain();
|
void recreateSwapchain();
|
||||||
void destroySwapchain();
|
void destroySwapchain();
|
||||||
@ -59,7 +62,6 @@ namespace nf::client::render {
|
|||||||
VkDescriptorSetLayout m_pipelineOutputDescriptorSetLayout;
|
VkDescriptorSetLayout m_pipelineOutputDescriptorSetLayout;
|
||||||
VkPipelineLayout m_pipelineOutputLayout;
|
VkPipelineLayout m_pipelineOutputLayout;
|
||||||
VkDescriptorPool m_pipelineOutputDescriptorPool;
|
VkDescriptorPool m_pipelineOutputDescriptorPool;
|
||||||
VkDescriptorSet m_pipelineOutputDescriptorSet;
|
|
||||||
VkPipeline m_pipelineOutput;
|
VkPipeline m_pipelineOutput;
|
||||||
|
|
||||||
// Execution objects
|
// Execution objects
|
||||||
@ -74,5 +76,8 @@ namespace nf::client::render {
|
|||||||
std::unique_ptr<Buffer> m_bufferVertex;
|
std::unique_ptr<Buffer> m_bufferVertex;
|
||||||
std::unique_ptr<Buffer> m_bufferIndex;
|
std::unique_ptr<Buffer> m_bufferIndex;
|
||||||
std::unique_ptr<Buffer> m_bufferUniformMVP;
|
std::unique_ptr<Buffer> m_bufferUniformMVP;
|
||||||
|
std::unique_ptr<Image> m_imageTest;
|
||||||
|
VkSampler m_sampler;
|
||||||
|
VkDescriptorSet m_pipelineOutputDescriptorSet;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
namespace nf::client::render {
|
namespace nf::client::render {
|
||||||
ShaderModule::ShaderModule(const VkDevice& device, const std::string& shaderBinary)
|
ShaderModule::ShaderModule(const VkDevice& device, const std::string& shaderBinary)
|
||||||
: Resource(device)
|
: GraphicsResource(device)
|
||||||
, m_shaderModule()
|
, m_shaderModule()
|
||||||
{
|
{
|
||||||
VkShaderModuleCreateInfo shaderModuleCI = { VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO };
|
VkShaderModuleCreateInfo shaderModuleCI = { VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO };
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
// ShaderModule class header
|
// ShaderModule class header
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Resource.h"
|
#include "GraphicsResource.h"
|
||||||
|
|
||||||
namespace nf::client::render {
|
namespace nf::client::render {
|
||||||
class ShaderModule : Resource {
|
class ShaderModule : GraphicsResource {
|
||||||
public:
|
public:
|
||||||
ShaderModule(const VkDevice& device, const std::string& shaderBinary);
|
ShaderModule(const VkDevice& device, const std::string& shaderBinary);
|
||||||
|
|
||||||
|
@ -16,23 +16,25 @@ namespace nf::client::render {
|
|||||||
, m_blocks()
|
, m_blocks()
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void VideoMemoryAllocator::allocateForBuffer(VkBuffer buffer, VkMemoryPropertyFlags memoryPropertyFlags, VkDeviceMemory& outDeviceMemory, VkDeviceSize& outOffset) {
|
void VideoMemoryAllocator::allocateForBuffer(VkBuffer buffer, VkMemoryPropertyFlags memoryPropertyFlags, VideoMemoryAllocation& outAllocation) {
|
||||||
VkMemoryRequirements memoryRequirements = {};
|
VkMemoryRequirements memoryRequirements = {};
|
||||||
vkGetBufferMemoryRequirements(m_device, buffer, &memoryRequirements);
|
vkGetBufferMemoryRequirements(m_device, buffer, &memoryRequirements);
|
||||||
|
|
||||||
VkMemoryAllocateInfo allocateInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
|
allocate(memoryRequirements, memoryPropertyFlags, outAllocation);
|
||||||
allocateInfo.allocationSize = memoryRequirements.size;
|
vkBindBufferMemory(m_device, buffer, outAllocation.memory, outAllocation.offset);
|
||||||
allocateInfo.memoryTypeIndex = findMemoryType(memoryRequirements.memoryTypeBits, memoryPropertyFlags);
|
|
||||||
|
|
||||||
if (allocateInfo.memoryTypeIndex == -1)
|
|
||||||
NFError("Could not find suitable memory type.");
|
|
||||||
|
|
||||||
allocateFromBlock(allocateInfo, memoryRequirements.alignment, outDeviceMemory, outOffset);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoMemoryAllocator::deallocate(VkDeviceMemory memoryBlock, VkDeviceSize offset) {
|
void VideoMemoryAllocator::allocateForImage(VkImage image, VkMemoryPropertyFlags memoryPropertyFlags, VideoMemoryAllocation& outAllocation) {
|
||||||
|
VkMemoryRequirements memoryRequirements = {};
|
||||||
|
vkGetImageMemoryRequirements(m_device, image, &memoryRequirements);
|
||||||
|
|
||||||
|
allocate(memoryRequirements, memoryPropertyFlags, outAllocation);
|
||||||
|
vkBindImageMemory(m_device, image, outAllocation.memory, outAllocation.offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoMemoryAllocator::deallocate(const VideoMemoryAllocation& allocation) {
|
||||||
const auto blockIterator = std::find_if(m_blocks.begin(), m_blocks.end(), [&](const auto& currentBlock) {
|
const auto blockIterator = std::find_if(m_blocks.begin(), m_blocks.end(), [&](const auto& currentBlock) {
|
||||||
if (currentBlock.memory == memoryBlock)
|
if (currentBlock.memory == allocation.memory)
|
||||||
return true;
|
return true;
|
||||||
else return false;
|
else return false;
|
||||||
});
|
});
|
||||||
@ -42,7 +44,7 @@ namespace nf::client::render {
|
|||||||
|
|
||||||
MemoryBlock& block = *blockIterator;
|
MemoryBlock& block = *blockIterator;
|
||||||
|
|
||||||
const auto allocationIterator = block.allocations.find(offset);
|
const auto allocationIterator = block.allocations.find(allocation.offset);
|
||||||
|
|
||||||
if (allocationIterator == block.allocations.end())
|
if (allocationIterator == block.allocations.end())
|
||||||
NFError("Could not find video memory allocation to deallocate.");
|
NFError("Could not find video memory allocation to deallocate.");
|
||||||
@ -56,32 +58,28 @@ namespace nf::client::render {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t VideoMemoryAllocator::findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags desiredPropertyFlags) {
|
void VideoMemoryAllocator::allocate(VkMemoryRequirements memoryRequirements, VkMemoryPropertyFlags memoryPropertyFlags, VideoMemoryAllocation& outAllocation) {
|
||||||
VkPhysicalDeviceMemoryProperties memoryProperties = {};
|
uint32_t memoryTypeIndex = findMemoryType(memoryRequirements.memoryTypeBits, memoryPropertyFlags);
|
||||||
vkGetPhysicalDeviceMemoryProperties(m_physicalDevice, &memoryProperties);
|
|
||||||
|
|
||||||
for (int i = 0; i < memoryProperties.memoryTypeCount; i++)
|
if (memoryTypeIndex == -1)
|
||||||
if (typeFilter & (1 << i) && (memoryProperties.memoryTypes[i].propertyFlags & desiredPropertyFlags) == desiredPropertyFlags)
|
NFError("Could not find suitable memory type.");
|
||||||
return i;
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void VideoMemoryAllocator::allocateFromBlock(VkMemoryAllocateInfo& allocateInfo, uint32_t alignment, VkDeviceMemory& outDeviceMemory, VkDeviceSize& outOffset) {
|
|
||||||
while (true) {
|
while (true) {
|
||||||
for (auto& block : m_blocks) {
|
for (auto& block : m_blocks) {
|
||||||
if (block.memoryTypeIndex == allocateInfo.memoryTypeIndex) {
|
if (block.memoryTypeIndex == memoryTypeIndex) {
|
||||||
VkDeviceSize lastOffset = 0, lastSize = 0;
|
VkDeviceSize lastOffset = 0, lastSize = 0;
|
||||||
// Check between existing allocations
|
// Check between existing allocations
|
||||||
for (const auto& [currentOffset, currentSize] : block.allocations) {
|
for (const auto& [currentOffset, currentSize] : block.allocations) {
|
||||||
// Check for alignment
|
// Check for alignment
|
||||||
VkDeviceSize checkOffset = lastOffset + lastSize, remainder = checkOffset % alignment;
|
VkDeviceSize checkOffset = lastOffset + lastSize, remainder = checkOffset % memoryRequirements.alignment;
|
||||||
checkOffset = remainder ? checkOffset + (alignment - remainder) : checkOffset;
|
checkOffset = remainder ? checkOffset + (memoryRequirements.alignment - remainder) : checkOffset;
|
||||||
// Check for enough space
|
// Check for enough space
|
||||||
if (currentOffset - lastOffset - lastSize >= allocateInfo.allocationSize) {
|
if (currentOffset - lastOffset - lastSize >= memoryRequirements.size) {
|
||||||
outDeviceMemory = block.memory;
|
outAllocation.memory = block.memory;
|
||||||
outOffset = checkOffset;
|
outAllocation.offset = checkOffset;
|
||||||
block.allocations[checkOffset] = allocateInfo.allocationSize;
|
if (block.mappedMemoryPointer)
|
||||||
|
outAllocation.mappedMemoryPointer = reinterpret_cast<char*>(block.mappedMemoryPointer) + outAllocation.offset;
|
||||||
|
block.allocations[checkOffset] = memoryRequirements.size;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,12 +87,14 @@ namespace nf::client::render {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check end
|
// Check end
|
||||||
VkDeviceSize checkOffset = lastOffset + lastSize, remainder = checkOffset % alignment;
|
VkDeviceSize checkOffset = lastOffset + lastSize, remainder = checkOffset % memoryRequirements.alignment;
|
||||||
checkOffset = remainder ? checkOffset + (alignment - remainder) : checkOffset;
|
checkOffset = remainder ? checkOffset + (memoryRequirements.alignment - remainder) : checkOffset;
|
||||||
if ((block.size - 1) - lastOffset - lastSize >= allocateInfo.allocationSize) {
|
if ((block.size - 1) - lastOffset - lastSize >= memoryRequirements.size) {
|
||||||
outDeviceMemory = block.memory;
|
outAllocation.memory = block.memory;
|
||||||
outOffset = checkOffset;
|
outAllocation.offset = checkOffset;
|
||||||
block.allocations[checkOffset] = allocateInfo.allocationSize;
|
if (block.mappedMemoryPointer)
|
||||||
|
outAllocation.mappedMemoryPointer = reinterpret_cast<char*>(block.mappedMemoryPointer) + outAllocation.offset;
|
||||||
|
block.allocations[checkOffset] = memoryRequirements.size;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,12 +105,27 @@ namespace nf::client::render {
|
|||||||
|
|
||||||
// No suitable block found, allocate one
|
// No suitable block found, allocate one
|
||||||
VkMemoryAllocateInfo newBlockAllocateInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
|
VkMemoryAllocateInfo newBlockAllocateInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
|
||||||
newBlockAllocateInfo.memoryTypeIndex = allocateInfo.memoryTypeIndex;
|
newBlockAllocateInfo.memoryTypeIndex = memoryTypeIndex;
|
||||||
newBlockAllocateInfo.allocationSize = calculateBlockSize(allocateInfo.memoryTypeIndex);
|
newBlockAllocateInfo.allocationSize = calculateBlockSize(memoryTypeIndex);
|
||||||
allocateBlock(newBlockAllocateInfo);
|
allocateBlock(newBlockAllocateInfo);
|
||||||
|
|
||||||
|
// If host visible, map
|
||||||
|
if (memoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
|
||||||
|
vkMapMemory(m_device, m_blocks.back().memory, 0, VK_WHOLE_SIZE, 0, &m_blocks.back().mappedMemoryPointer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t VideoMemoryAllocator::findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags desiredPropertyFlags) {
|
||||||
|
VkPhysicalDeviceMemoryProperties memoryProperties = {};
|
||||||
|
vkGetPhysicalDeviceMemoryProperties(m_physicalDevice, &memoryProperties);
|
||||||
|
|
||||||
|
for (int i = 0; i < memoryProperties.memoryTypeCount; i++)
|
||||||
|
if (typeFilter & (1 << i) && (memoryProperties.memoryTypes[i].propertyFlags & desiredPropertyFlags) == desiredPropertyFlags)
|
||||||
|
return i;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
size_t VideoMemoryAllocator::calculateBlockSize(uint32_t memoryTypeIndex) const {
|
size_t VideoMemoryAllocator::calculateBlockSize(uint32_t memoryTypeIndex) const {
|
||||||
VkPhysicalDeviceMemoryProperties memoryProperties = {};
|
VkPhysicalDeviceMemoryProperties memoryProperties = {};
|
||||||
vkGetPhysicalDeviceMemoryProperties(m_physicalDevice, &memoryProperties);
|
vkGetPhysicalDeviceMemoryProperties(m_physicalDevice, &memoryProperties);
|
||||||
@ -125,6 +140,6 @@ namespace nf::client::render {
|
|||||||
if (vkAllocateMemory(m_device, &allocateInfo, nullptr, &memoryBlock) != VK_SUCCESS)
|
if (vkAllocateMemory(m_device, &allocateInfo, nullptr, &memoryBlock) != VK_SUCCESS)
|
||||||
NFError("Could not allocate video memory.");
|
NFError("Could not allocate video memory.");
|
||||||
|
|
||||||
m_blocks.emplace_back(memoryBlock, allocateInfo.memoryTypeIndex, allocateInfo.allocationSize);
|
m_blocks.emplace_back(memoryBlock, allocateInfo.memoryTypeIndex, allocateInfo.allocationSize, nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,10 +2,17 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace nf::client::render {
|
namespace nf::client::render {
|
||||||
|
struct VideoMemoryAllocation {
|
||||||
|
VkDeviceMemory memory;
|
||||||
|
VkDeviceSize offset;
|
||||||
|
void* mappedMemoryPointer;
|
||||||
|
};
|
||||||
|
|
||||||
struct MemoryBlock {
|
struct MemoryBlock {
|
||||||
VkDeviceMemory memory;
|
VkDeviceMemory memory;
|
||||||
uint32_t memoryTypeIndex;
|
uint32_t memoryTypeIndex;
|
||||||
size_t size;
|
size_t size;
|
||||||
|
void* mappedMemoryPointer;
|
||||||
|
|
||||||
std::map<VkDeviceSize, VkDeviceSize> allocations;
|
std::map<VkDeviceSize, VkDeviceSize> allocations;
|
||||||
// Offset Size
|
// Offset Size
|
||||||
@ -15,12 +22,14 @@ namespace nf::client::render {
|
|||||||
public:
|
public:
|
||||||
VideoMemoryAllocator(const VkDevice& device, const VkPhysicalDevice& physicalDevice);
|
VideoMemoryAllocator(const VkDevice& device, const VkPhysicalDevice& physicalDevice);
|
||||||
|
|
||||||
void allocateForBuffer(VkBuffer buffer, VkMemoryPropertyFlags memoryPropertyFlags, VkDeviceMemory& outDeviceMemory, VkDeviceSize& outOffset);
|
void allocateForBuffer(VkBuffer buffer, VkMemoryPropertyFlags memoryPropertyFlags, VideoMemoryAllocation& outAllocation);
|
||||||
void deallocate(VkDeviceMemory memoryBlock, VkDeviceSize offset);
|
void allocateForImage(VkImage image, VkMemoryPropertyFlags memoryPropertyFlags, VideoMemoryAllocation& outAllocation);
|
||||||
|
|
||||||
|
void deallocate(const VideoMemoryAllocation& allocation);
|
||||||
private:
|
private:
|
||||||
|
void allocate(VkMemoryRequirements memoryRequirements, VkMemoryPropertyFlags memoryPropertyFlags, VideoMemoryAllocation& outAllocation);
|
||||||
uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags desiredPropertyFlags);
|
uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags desiredPropertyFlags);
|
||||||
|
|
||||||
void allocateFromBlock(VkMemoryAllocateInfo& allocateInfo, uint32_t alignment, VkDeviceMemory& outDeviceMemory, VkDeviceSize& outOffset);
|
|
||||||
size_t calculateBlockSize(uint32_t memoryTypeIndex) const;
|
size_t calculateBlockSize(uint32_t memoryTypeIndex) const;
|
||||||
void allocateBlock(VkMemoryAllocateInfo& allocateInfo);
|
void allocateBlock(VkMemoryAllocateInfo& allocateInfo);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user