Skip to content

Commit

Permalink
Implement Texture binding system for Uniforms
Browse files Browse the repository at this point in the history
* Rename VkResult_log to VkResult_check
* Remove verbose documentation for internal submodules (annoying to maintain)
  • Loading branch information
SirBob01 committed Oct 31, 2024
1 parent 660657f commit 32a3a04
Show file tree
Hide file tree
Showing 24 changed files with 837 additions and 685 deletions.
2 changes: 1 addition & 1 deletion src/Graphics/Mesh.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ namespace Dynamo::Graphics {
*
*/
struct MeshDescriptor {
using AttributeBuffer = std::vector<char>;
using AttributeBuffer = std::vector<unsigned char>;

std::vector<AttributeBuffer> attributes;
unsigned vertex_count;
Expand Down
31 changes: 22 additions & 9 deletions src/Graphics/Renderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace Dynamo::Graphics {
_surface = _display.create_vulkan_surface(_instance);

// Create the logical device
_physical = PhysicalDevice::select(_instance, _surface);
_physical = PhysicalDevice::select_best(_instance, _surface);
_device = VkDevice_create(_physical);

// Build the swapchain and its views
Expand All @@ -24,6 +24,7 @@ namespace Dynamo::Graphics {
_shaders = ShaderRegistry(_device);
_materials = MaterialRegistry(_device, root_asset_directory + "/vulkan_cache.bin");
_uniforms = UniformRegistry(_device, _physical, _transfer_pool);
_textures = TextureRegistry(_device, _physical, _transfer_pool);
_framebuffers = FramebufferCache(_device);

// Frame contexts
Expand All @@ -46,6 +47,7 @@ namespace Dynamo::Graphics {
// High-level objects
_frame_contexts.destroy();
_framebuffers.destroy();
_textures.destroy();
_uniforms.destroy();
_materials.destroy();
_shaders.destroy();
Expand Down Expand Up @@ -87,6 +89,10 @@ namespace Dynamo::Graphics {

void Renderer::destroy_shader(Shader shader) { _shaders.destroy(shader); }

Texture Renderer::build_texture(const TextureDescriptor &descriptor) { return _textures.build(descriptor); }

void Renderer::destroy_texture(Texture texture) { _textures.destroy(texture); }

Material Renderer::build_material(const MaterialDescriptor &descriptor) {
return _materials.build(descriptor, _swapchain, _shaders, _uniforms);
}
Expand All @@ -104,7 +110,14 @@ namespace Dynamo::Graphics {
return {};
}

void Renderer::write_uniform(Uniform uniform, void *data) { _uniforms.write(uniform, data); }
void Renderer::write_uniform(Uniform uniform, void *data, unsigned index, unsigned count) {
_uniforms.write(uniform, data, index, count);
}

void Renderer::bind_texture(Uniform uniform, Texture texture, unsigned index) {
const TextureInstance &instance = _textures.get(texture);
_uniforms.bind(uniform, instance, index);
}

void Renderer::draw(const Model &model) { _models.push_back(model); }

Expand All @@ -123,18 +136,18 @@ namespace Dynamo::Graphics {
rebuild_swapchain();
return;
} else if (acquire_result != VK_SUCCESS && acquire_result != VK_SUBOPTIMAL_KHR) {
VkResult_log("Acquire Image", acquire_result);
VkResult_check("Acquire Image", acquire_result);
}

VkResult_log("Reset Fence", vkResetFences(_device, 1, &frame.sync_fence));
VkResult_log("Reset Command Buffer", vkResetCommandBuffer(frame.command_buffer, 0));
VkResult_check("Reset Fence", vkResetFences(_device, 1, &frame.sync_fence));
VkResult_check("Reset Command Buffer", vkResetCommandBuffer(frame.command_buffer, 0));

VkCommandBufferBeginInfo begin_info = {};
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
begin_info.flags = 0;
begin_info.pInheritanceInfo = nullptr;

VkResult_log("Begin Command Recording", vkBeginCommandBuffer(frame.command_buffer, &begin_info));
VkResult_check("Begin Command Recording", vkBeginCommandBuffer(frame.command_buffer, &begin_info));

// Group models by material and geometry
std::sort(_models.begin(), _models.end(), [](const Model &a, const Model &b) {
Expand Down Expand Up @@ -248,7 +261,7 @@ namespace Dynamo::Graphics {
if (prev_renderpass != VK_NULL_HANDLE) {
vkCmdEndRenderPass(frame.command_buffer);
}
VkResult_log("End Command Buffer", vkEndCommandBuffer(frame.command_buffer));
VkResult_check("End Command Buffer", vkEndCommandBuffer(frame.command_buffer));

// Submit commands
VkQueue queue;
Expand All @@ -265,7 +278,7 @@ namespace Dynamo::Graphics {
submit_info.pSignalSemaphores = &frame.sync_render_done;
submit_info.pWaitDstStageMask = &wait_stage_mask;

VkResult_log("Graphics Submit", vkQueueSubmit(queue, 1, &submit_info, frame.sync_fence));
VkResult_check("Graphics Submit", vkQueueSubmit(queue, 1, &submit_info, frame.sync_fence));

// Present the render
VkPresentInfoKHR present_info = {};
Expand All @@ -282,7 +295,7 @@ namespace Dynamo::Graphics {
if (present_result == VK_ERROR_OUT_OF_DATE_KHR || present_result == VK_SUBOPTIMAL_KHR) {
rebuild_swapchain();
} else if (present_result != VK_SUCCESS) {
VkResult_log("Present Render", present_result);
VkResult_check("Present Render", present_result);
}
}
} // namespace Dynamo::Graphics
43 changes: 39 additions & 4 deletions src/Graphics/Renderer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <Display.hpp>
#include <Graphics/Mesh.hpp>
#include <Graphics/Model.hpp>
#include <Graphics/Texture.hpp>
#include <Graphics/Vulkan/Buffer.hpp>
#include <Graphics/Vulkan/FrameContext.hpp>
#include <Graphics/Vulkan/FramebufferCache.hpp>
Expand All @@ -13,6 +14,7 @@
#include <Graphics/Vulkan/PhysicalDevice.hpp>
#include <Graphics/Vulkan/ShaderRegistry.hpp>
#include <Graphics/Vulkan/Swapchain.hpp>
#include <Graphics/Vulkan/TextureRegistry.hpp>
#include <Graphics/Vulkan/UniformRegistry.hpp>
#include <Math/Color.hpp>

Expand Down Expand Up @@ -42,6 +44,7 @@ namespace Dynamo::Graphics {
ShaderRegistry _shaders;
MaterialRegistry _materials;
UniformRegistry _uniforms;
TextureRegistry _textures;
FramebufferCache _framebuffers;

FrameContextList _frame_contexts;
Expand All @@ -50,12 +53,16 @@ namespace Dynamo::Graphics {
VkClearValue _clear;

// TODO - Fixes:
// * Buffer has a lot of problems... Resizing will invalidate handles used by other resources
// - Instead of Buffer, build a MemoryPool that tracks allocated VkBuffers / VkImages
// - A VkBuffer should persist throughout program lifetime to prevent messy rebuilding of allocated resources
// - If a VkBuffer is full, create a new one. Need a sane minimum size to prevent overallocation (limit 4096)
// - Allow passing in fallback memory types when allocating memory
// * Pre-defined render pass ---- Define a default render pass to handle the no-draw case
// * Let Buffer take in fallback memory types, only throw when all options exhausted

// TODO - Features:
// * Live update texture? --- Support non-shader-optimal image layouts
// * Depth-stencil buffer ---- Update jukebox to showcase 3d perspective (to visualize depth buffering)
// * Texture system ---- Similar to shaders / meshes, generate a handle and return
// * Draw-to-texture ---- overload render(), render(Texture texture)

/**
Expand Down Expand Up @@ -116,6 +123,21 @@ namespace Dynamo::Graphics {
*/
void destroy_shader(Shader shader);

/**
* @brief Build a texture.
*
* @param descriptor
* @return Texture
*/
Texture build_texture(const TextureDescriptor &descriptor);

/**
* @brief Free texture resources.
*
* @param texture
*/
void destroy_texture(Texture texture);

/**
* @brief Build a material.
*
Expand Down Expand Up @@ -143,12 +165,25 @@ namespace Dynamo::Graphics {
/**
* @brief Write to a uniform.
*
* Data must match the size of the uniform variable.
* If the uniform is an array, an index offset and count can be provided.
*
* @param uniform
* @param data
* @param index
* @param count
*/
void write_uniform(Uniform uniform, void *data, unsigned index = 0, unsigned count = 1);

/**
* @brief Bind a texture to a uniform variable.
*
* If the uniform is an array, an index offset can be provided.
*
* @param uniform
* @param texture
* @param index
*/
void write_uniform(Uniform uniform, void *data);
void bind_texture(Uniform uniform, Texture texture, unsigned index = 0);

/**
* @brief Draw a model in the current frame.
Expand Down
104 changes: 104 additions & 0 deletions src/Graphics/Texture.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#pragma once

#include <vector>

#include <Utils/SparseArray.hpp>

namespace Dynamo::Graphics {
/**
* @brief Texture resource handle.
*
*/
DYN_DEFINE_ID_TYPE(Texture);

/**
* @brief Texture formats.
*
*/
enum class TextureFormat {
F32_R_Norm,
U8_RGB_Norm,
U8_RGBA_Norm,
};

/**
* @brief Texture filter modes.
*
*/
enum class TextureFilter {
Nearest,
Linear,
};

/**
* @brief Texture addressing modes.
*
*/
enum class TextureAddressMode {
Repeat,
RepeatMirror,
Clamp,
ClampMirror,
ClampBorder,
};

/**
* @brief Texture descriptor.
*
*/
struct TextureDescriptor {
/**
* @brief Texture unit byte buffer.
*
*/
std::vector<unsigned char> texels;

/**
* @brief Width of the texture in texture units.
*
*/
unsigned width = 0;

/**
* @brief Height of the texture in texture units.
*
*/
unsigned height = 0;

/**
* @brief Format of the texture determines how the byte buffer is interpreted.
*
*/
TextureFormat format = TextureFormat::U8_RGBA_Norm;

/**
* @brief Minification filter.
*
*/
TextureFilter min_filter = TextureFilter::Nearest;

/**
* @brief Magnification filter.
*
*/
TextureFilter mag_filter = TextureFilter::Nearest;

/**
* @brief How U coordinates are addressed outside [0, 1).
*
*/
TextureAddressMode u_address_mode = TextureAddressMode::Repeat;

/**
* @brief How V coordinates are addressed outside [0, 1).
*
*/
TextureAddressMode v_address_mode = TextureAddressMode::Repeat;

/**
* @brief How W coordinates are addressed outside [0, 1).
*
*/
TextureAddressMode w_address_mode = TextureAddressMode::Repeat;
};
} // namespace Dynamo::Graphics
15 changes: 10 additions & 5 deletions src/Graphics/Vulkan/Buffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace Dynamo::Graphics::Vulkan {
VkBufferUsageFlagBits usage,
VkMemoryPropertyFlags properties) :
_device(device),
_physical_settings(physical.memory), _command_buffer(command_buffer),
_physical(&physical), _command_buffer(command_buffer),
_usage(usage | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT), _properties(properties),
_allocator(MIN_ALLOCATION_SIZE) {
_handle = VkBuffer_create(_device, _usage, _allocator.capacity(), nullptr, 0);
Expand All @@ -21,20 +21,20 @@ namespace Dynamo::Graphics::Vulkan {
vkGetBufferMemoryRequirements(_device, buffer, &_requirements);

unsigned type_index;
for (type_index = 0; type_index < _physical_settings.memoryTypeCount; type_index++) {
VkMemoryType type = _physical_settings.memoryTypes[type_index];
for (type_index = 0; type_index < _physical->memory.memoryTypeCount; type_index++) {
VkMemoryType type = _physical->memory.memoryTypes[type_index];
if ((_requirements.memoryTypeBits & (1 << type_index)) &&
((_properties & type.propertyFlags) == _properties)) {
break;
}
}
if (type_index == _physical_settings.memoryTypeCount) {
if (type_index == _physical->memory.memoryTypeCount) {
Log::error("Vulkan could not find suitable memory type for buffer.");
}

VkDeviceMemory memory = VkDeviceMemory_allocate(_device, type_index, _requirements.size);
if (_properties & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {
VkResult_log(
VkResult_check(
"Map Device Memory",
vkMapMemory(_device, memory, 0, _allocator.capacity(), 0, reinterpret_cast<void **>(&_mapped)));
DYN_ASSERT(_mapped != nullptr);
Expand Down Expand Up @@ -106,6 +106,11 @@ namespace Dynamo::Graphics::Vulkan {
vkQueueWaitIdle(_transfer_queue);
}

void Buffer::copy_to(VkImage &dst, unsigned offset, VkBufferImageCopy *regions, unsigned region_count) {
VkBuffer_immediate_image_copy(_handle, dst, _transfer_queue, _command_buffer, regions, region_count);
vkQueueWaitIdle(_transfer_queue);
}

void Buffer::destroy() {
vkDestroyBuffer(_device, _handle, nullptr);
vkFreeMemory(_device, _memory, nullptr);
Expand Down
Loading

0 comments on commit 32a3a04

Please sign in to comment.