.
This commit is contained in:
parent
a015aa00ef
commit
a0b7e4c0d8
0
metal/CMakeLists.txt
Normal file
0
metal/CMakeLists.txt
Normal file
3
metal/definition.cpp
Normal file
3
metal/definition.cpp
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
//
|
||||||
|
// Created by Vicente Ferrari Smith on 26.02.26.
|
||||||
|
//
|
||||||
3
renderer/graphics.cpp
Normal file
3
renderer/graphics.cpp
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
//
|
||||||
|
// Created by Vicente Ferrari Smith on 02.03.26.
|
||||||
|
//
|
||||||
14
renderer/graphics.h
Normal file
14
renderer/graphics.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
//
|
||||||
|
// Created by Vicente Ferrari Smith on 26.02.26.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef V_RENDERER_H
|
||||||
|
#define V_RENDERER_H
|
||||||
|
|
||||||
|
|
||||||
|
struct Graphics {
|
||||||
|
Graphics();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //V_RENDERER_H
|
||||||
8
renderer/graphics_private.h
Normal file
8
renderer/graphics_private.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
//
|
||||||
|
// Created by Vicente Ferrari Smith on 02.03.26.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef V_GRAPHICS_PRIVATE_H
|
||||||
|
#define V_GRAPHICS_PRIVATE_H
|
||||||
|
|
||||||
|
#endif //V_GRAPHICS_PRIVATE_H
|
||||||
@ -1,47 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by Vicente Ferrari Smith on 12.02.26.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef V_INIT_H
|
|
||||||
#define V_INIT_H
|
|
||||||
|
|
||||||
#include <volk/volk.h>
|
|
||||||
#include <GLFW/glfw3.h>
|
|
||||||
#include <vma/vk_mem_alloc.h>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
inline VkInstance instance{};
|
|
||||||
inline VkPhysicalDevice physicalDevice{};
|
|
||||||
inline VkDevice device{};
|
|
||||||
inline VkQueue graphics_queue{};
|
|
||||||
inline uint32_t queueFamily{};
|
|
||||||
|
|
||||||
inline VkSurfaceKHR surface{};
|
|
||||||
inline VkDebugUtilsMessengerEXT debugMessenger{};
|
|
||||||
|
|
||||||
inline VmaAllocator allocator{};
|
|
||||||
|
|
||||||
inline constexpr uint32_t MAX_FRAMES_IN_FLIGHT = 2;
|
|
||||||
inline constexpr uint32_t MAX_VERTICES_PER_BATCH = 65536;
|
|
||||||
|
|
||||||
inline VkSwapchainKHR swapchain;
|
|
||||||
inline VkExtent2D swapchain_extent;
|
|
||||||
inline VkSurfaceFormatKHR swapchain_format{
|
|
||||||
VK_FORMAT_B8G8R8A8_UNORM,
|
|
||||||
VK_COLOR_SPACE_SRGB_NONLINEAR_KHR
|
|
||||||
};
|
|
||||||
inline std::vector<VkSemaphore> renderFinished;
|
|
||||||
|
|
||||||
inline std::vector<VkImage> images;
|
|
||||||
inline std::vector<VkImageView> imageViews;
|
|
||||||
inline std::vector<VkImageLayout> imageLayouts;
|
|
||||||
|
|
||||||
void createSwapchain(GLFWwindow* window);
|
|
||||||
|
|
||||||
int createInstance(GLFWwindow* window);
|
|
||||||
void createSurface(GLFWwindow* window);
|
|
||||||
void pickPhysicalDevice();
|
|
||||||
void createDevice();
|
|
||||||
|
|
||||||
|
|
||||||
#endif //V_INIT_H
|
|
||||||
3
renderer/metal/AAPLMathUtilities.cpp
Normal file
3
renderer/metal/AAPLMathUtilities.cpp
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
//
|
||||||
|
// Created by Vicente Ferrari Smith on 27.02.26.
|
||||||
|
//
|
||||||
8
renderer/metal/AAPLMathUtilities.h
Normal file
8
renderer/metal/AAPLMathUtilities.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
//
|
||||||
|
// Created by Vicente Ferrari Smith on 27.02.26.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef V_AAPLMATHUTILITIES_H
|
||||||
|
#define V_AAPLMATHUTILITIES_H
|
||||||
|
|
||||||
|
#endif //V_AAPLMATHUTILITIES_H
|
||||||
216
renderer/metal/metal.cpp
Normal file
216
renderer/metal/metal.cpp
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
//
|
||||||
|
// Created by Vicente Ferrari Smith on 26.02.26.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "init.h"
|
||||||
|
#include "../graphics.h"
|
||||||
|
#include <print>
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
#define GLFW_EXPOSE_NATIVE_COCOA
|
||||||
|
#include <GLFW/glfw3native.h>
|
||||||
|
#include <objc/message.h>
|
||||||
|
#include <objc/objc.h>
|
||||||
|
#include "vertex_data.h"
|
||||||
|
|
||||||
|
Device metal_device{};
|
||||||
|
MTL::Buffer* triangle_vertex_buffer{};
|
||||||
|
MTL::CommandQueue *queue{};
|
||||||
|
CA::MetalLayer *metal_layer{};
|
||||||
|
MTL::RenderPipelineState *pipeline_state{};
|
||||||
|
CA::MetalDrawable *metal_drawable{};
|
||||||
|
MTL::CommandBuffer* metal_command_buffer{};
|
||||||
|
|
||||||
|
MTL::Function *vertex_shader{};
|
||||||
|
MTL::Function *fragment_shader{};
|
||||||
|
|
||||||
|
void create_window(GLFWwindow *window) {
|
||||||
|
void *ns_window = glfwGetCocoaWindow(window);
|
||||||
|
if (!ns_window) {
|
||||||
|
throw std::runtime_error("Failed to get Cocoa window from GLFWwindow");
|
||||||
|
}
|
||||||
|
|
||||||
|
SEL contentViewSel = sel_registerName("contentView");
|
||||||
|
id content_view = ((id (*)(id, SEL))objc_msgSend)((id)ns_window, contentViewSel);
|
||||||
|
|
||||||
|
SEL setWantsLayerSel = sel_registerName("setWantsLayer:");
|
||||||
|
((void (*)(id, SEL, bool))objc_msgSend)(content_view, setWantsLayerSel, true);
|
||||||
|
|
||||||
|
metal_layer = CA::MetalLayer::layer()->retain();
|
||||||
|
|
||||||
|
SEL setLayerSel = sel_registerName("setLayer:");
|
||||||
|
((void (*)(id, SEL, id))objc_msgSend)(content_view, setLayerSel, (id)metal_layer);
|
||||||
|
|
||||||
|
metal_layer->retain();
|
||||||
|
metal_layer->setDevice(metal_device.device);
|
||||||
|
metal_layer->setPixelFormat(MTL::PixelFormatRGBA16Float);
|
||||||
|
metal_layer->setFramebufferOnly(true);
|
||||||
|
metal_layer->setDrawableSize(CGSizeMake(800, 600));
|
||||||
|
CGColorSpaceRef p3Space = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
|
||||||
|
metal_layer->setColorspace(p3Space);
|
||||||
|
}
|
||||||
|
|
||||||
|
void encode_render_command(MTL::RenderCommandEncoder *renderCommandEncoder) {
|
||||||
|
renderCommandEncoder->setRenderPipelineState(pipeline_state);
|
||||||
|
renderCommandEncoder->setVertexBuffer(triangle_vertex_buffer, 0, 0);
|
||||||
|
MTL::PrimitiveType typeTriangle = MTL::PrimitiveTypeTriangle;
|
||||||
|
NS::UInteger vertexStart = 0;
|
||||||
|
NS::UInteger vertexCount = 6;
|
||||||
|
renderCommandEncoder->drawPrimitives(typeTriangle, vertexStart, vertexCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
void send_render_command() {
|
||||||
|
metal_command_buffer = queue->commandBuffer();
|
||||||
|
|
||||||
|
MTL::RenderPassDescriptor* renderPassDescriptor = MTL::RenderPassDescriptor::alloc()->init();
|
||||||
|
MTL::RenderPassColorAttachmentDescriptor *cd = renderPassDescriptor->colorAttachments()->object(0);
|
||||||
|
cd->setTexture(metal_drawable->texture());
|
||||||
|
cd->setLoadAction(MTL::LoadActionClear);
|
||||||
|
cd->setClearColor(MTL::ClearColor(
|
||||||
|
100.0f / 255.0f,
|
||||||
|
149.0f / 255.0f,
|
||||||
|
237.0f / 255.0f,
|
||||||
|
1.0
|
||||||
|
));
|
||||||
|
cd->setStoreAction(MTL::StoreActionStore);
|
||||||
|
|
||||||
|
MTL::RenderCommandEncoder* renderCommandEncoder = metal_command_buffer->renderCommandEncoder(renderPassDescriptor);
|
||||||
|
encode_render_command(renderCommandEncoder);
|
||||||
|
renderCommandEncoder->endEncoding();
|
||||||
|
|
||||||
|
metal_command_buffer->presentDrawable(metal_drawable);
|
||||||
|
metal_command_buffer->commit();
|
||||||
|
metal_command_buffer->waitUntilCompleted();
|
||||||
|
|
||||||
|
renderPassDescriptor->release();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoadMetalShader(const std::string &shader_path,
|
||||||
|
const std::string &vertex_fn_name,
|
||||||
|
const std::string &fragment_fn_name)
|
||||||
|
{
|
||||||
|
NS::Error *error = nullptr;
|
||||||
|
MTL::Library *library = nullptr;
|
||||||
|
|
||||||
|
auto ends_with = [](const std::string& s, const char* suf) -> bool {
|
||||||
|
const size_t n = std::strlen(suf);
|
||||||
|
return s.size() >= n && s.compare(s.size() - n, n, suf) == 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (ends_with(shader_path, ".metal")) {
|
||||||
|
// Compile from source at runtime
|
||||||
|
std::ifstream file(shader_path, std::ios::in | std::ios::binary);
|
||||||
|
if (!file.is_open()) {
|
||||||
|
throw std::runtime_error("Failed to open .metal source file");
|
||||||
|
}
|
||||||
|
std::string src;
|
||||||
|
file.seekg(0, std::ios::end);
|
||||||
|
src.resize(static_cast<size_t>(file.tellg()));
|
||||||
|
file.seekg(0, std::ios::beg);
|
||||||
|
file.read(src.data(), static_cast<std::streamsize>(src.size()));
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
NS::String* source = NS::String::string(src.c_str(), NS::UTF8StringEncoding);
|
||||||
|
MTL::CompileOptions* opts = MTL::CompileOptions::alloc()->init();
|
||||||
|
library = metal_device.device->newLibrary(source, opts, &error);
|
||||||
|
opts->release();
|
||||||
|
} else {
|
||||||
|
// Load a precompiled metallib from file path
|
||||||
|
NS::String *nsPath = NS::String::string(shader_path.c_str(), NS::UTF8StringEncoding);
|
||||||
|
library = metal_device.device->newLibrary(nsPath, &error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error || library == nullptr) {
|
||||||
|
if (error) {
|
||||||
|
// Extract the actual compiler error message
|
||||||
|
const char* errorMessage = error->localizedDescription()->utf8String();
|
||||||
|
std::string detailedError = "Metal Library Error: ";
|
||||||
|
detailedError += errorMessage;
|
||||||
|
|
||||||
|
// It is good practice to release the error object if it exists
|
||||||
|
error->release();
|
||||||
|
|
||||||
|
throw std::runtime_error(detailedError);
|
||||||
|
}
|
||||||
|
throw std::runtime_error("Failed to create Metal library (Unknown error)");
|
||||||
|
}
|
||||||
|
NS::String *vname = NS::String::string(vertex_fn_name.c_str(), NS::UTF8StringEncoding);
|
||||||
|
NS::String *fname = NS::String::string(fragment_fn_name.c_str(), NS::UTF8StringEncoding);
|
||||||
|
vertex_shader = library->newFunction(vname);
|
||||||
|
fragment_shader = library->newFunction(fname);
|
||||||
|
|
||||||
|
if (vertex_shader == nullptr || fragment_shader == nullptr) {
|
||||||
|
throw std::runtime_error("Failed to create Metal shader functions");
|
||||||
|
}
|
||||||
|
|
||||||
|
library->release();
|
||||||
|
}
|
||||||
|
|
||||||
|
void create_render_pipeline() {
|
||||||
|
LoadMetalShader("shaders/shader.metal", "vertex_main", "fragment_main");
|
||||||
|
|
||||||
|
MTL::RenderPipelineDescriptor* renderPipelineDescriptor = MTL::RenderPipelineDescriptor::alloc()->init();
|
||||||
|
renderPipelineDescriptor->setLabel(NS::String::string("Triangle Rendering Pipeline", NS::ASCIIStringEncoding));
|
||||||
|
renderPipelineDescriptor->setVertexFunction(vertex_shader);
|
||||||
|
renderPipelineDescriptor->setFragmentFunction(fragment_shader);
|
||||||
|
assert(renderPipelineDescriptor);
|
||||||
|
const MTL::PixelFormat pixel_format = metal_layer->pixelFormat();
|
||||||
|
renderPipelineDescriptor->colorAttachments()->object(0)->setPixelFormat(pixel_format);
|
||||||
|
|
||||||
|
NS::Error* error;
|
||||||
|
pipeline_state = metal_device.device->newRenderPipelineState(renderPipelineDescriptor, &error);
|
||||||
|
renderPipelineDescriptor->release();
|
||||||
|
}
|
||||||
|
|
||||||
|
void create_command_queue() {
|
||||||
|
queue = metal_device.device->newCommandQueue();
|
||||||
|
}
|
||||||
|
|
||||||
|
void create_triangle() {
|
||||||
|
VertexData square_vertices[] = {
|
||||||
|
{{-0.5, -0.5}, {1.0, 0.0, 0.0, 1.0}},
|
||||||
|
{{0.5, -0.5}, {0.0, 1.0, 0.0, 1.0}},
|
||||||
|
{{0.5, 0.5}, {0.0, 0.0, 1.0, 1.0}},
|
||||||
|
|
||||||
|
{{0.5, 0.5}, {0.0, 0.0, 1.0, 1.0}},
|
||||||
|
{{-0.5, 0.5}, {0.0, 1.0, 0.0, 1.0}},
|
||||||
|
{{-0.5, -0.5}, {1.0, 0.0, 0.0, 1.0}},
|
||||||
|
};
|
||||||
|
|
||||||
|
triangle_vertex_buffer = metal_device.device->newBuffer(&square_vertices,
|
||||||
|
sizeof(square_vertices),
|
||||||
|
MTL::ResourceStorageModeShared);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void graphics_init(GLFWwindow *window) {
|
||||||
|
std::println("wow, we are on macos!! crazy!!");
|
||||||
|
|
||||||
|
create_device();
|
||||||
|
create_window(window);
|
||||||
|
create_triangle();
|
||||||
|
create_command_queue();
|
||||||
|
create_render_pipeline();
|
||||||
|
}
|
||||||
|
|
||||||
|
void graphics_deinit() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void begin_frame() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void end_frame() {
|
||||||
|
auto pPool = NS::AutoreleasePool::alloc()->init();
|
||||||
|
metal_drawable = metal_layer->nextDrawable();
|
||||||
|
|
||||||
|
send_render_command();
|
||||||
|
|
||||||
|
pPool->release();
|
||||||
|
}
|
||||||
|
|
||||||
|
void create_device() {
|
||||||
|
metal_device.device = MTL::CreateSystemDefaultDevice();
|
||||||
|
}
|
||||||
22
renderer/metal/metal.h
Normal file
22
renderer/metal/metal.h
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
//
|
||||||
|
// Created by Vicente Ferrari Smith on 26.02.26.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef M_INIT_H
|
||||||
|
#define M_INIT_H
|
||||||
|
|
||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
#define GLFW_EXPOSE_NATIVE_COCOA
|
||||||
|
#import <GLFW/glfw3native.h>
|
||||||
|
|
||||||
|
#include <Metal/Metal.hpp>
|
||||||
|
#include <QuartzCore/CAMetalLayer.hpp>
|
||||||
|
#include <QuartzCore/QuartzCore.hpp>
|
||||||
|
|
||||||
|
struct Device {
|
||||||
|
MTL::Device *device;
|
||||||
|
};
|
||||||
|
|
||||||
|
void create_device();
|
||||||
|
|
||||||
|
#endif //M_INIT_H
|
||||||
@ -2,12 +2,12 @@
|
|||||||
// Created by Vicente Ferrari Smith on 13.02.26.
|
// Created by Vicente Ferrari Smith on 13.02.26.
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "renderer.h"
|
#include "../graphics.h"
|
||||||
|
|
||||||
#include <print>
|
#include <print>
|
||||||
|
|
||||||
#include "init.h"
|
#include "init.h"
|
||||||
#include "sprite.h"
|
#include "../sprite.h"
|
||||||
#include <vma/vk_mem_alloc.h>
|
#include <vma/vk_mem_alloc.h>
|
||||||
#include <slang/slang.h>
|
#include <slang/slang.h>
|
||||||
|
|
||||||
402
renderer/metal/renderer.h
Normal file
402
renderer/metal/renderer.h
Normal file
@ -0,0 +1,402 @@
|
|||||||
|
//
|
||||||
|
// Created by Vicente Ferrari Smith on 13.02.26.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef V_RENDERER_H
|
||||||
|
#define V_RENDERER_H
|
||||||
|
|
||||||
|
#include "init.h"
|
||||||
|
#include <volk/volk.h>
|
||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
#define GLM_FORCE_RADIANS
|
||||||
|
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
|
||||||
|
#define GLM_ENABLE_EXPERIMENTAL
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include <glm/ext/matrix_clip_space.hpp>
|
||||||
|
#include "glm/gtx/string_cast.hpp"
|
||||||
|
#include <vma/vk_mem_alloc.h>
|
||||||
|
#include "../sprite.h"
|
||||||
|
#include "../texture.h"
|
||||||
|
#include <misc.h>
|
||||||
|
#include <array>
|
||||||
|
#include <span>
|
||||||
|
#include <slang/slang.h>
|
||||||
|
#include <slang/slang-com-ptr.h>
|
||||||
|
|
||||||
|
inline Slang::ComPtr<slang::IGlobalSession> slangGlobalSession;
|
||||||
|
|
||||||
|
enum class PROJECTION_TYPE : uint8_t {
|
||||||
|
NONE,
|
||||||
|
ORTHOGRAPHIC_WORLD,
|
||||||
|
ORTHOGRAPHIC_WINDOW,
|
||||||
|
PERSPECTIVE_WORLD,
|
||||||
|
PERSPECTIVE_WINDOW,
|
||||||
|
COUNT,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct vertex_p2_s2_st2_col4_a1_u32 {
|
||||||
|
glm::vec2 pos;
|
||||||
|
glm::vec2 scale;
|
||||||
|
glm::vec2 uv;
|
||||||
|
glm::vec4 color;
|
||||||
|
float alpha;
|
||||||
|
uint32_t textureID;
|
||||||
|
|
||||||
|
static VkVertexInputBindingDescription getBindingDescription() {
|
||||||
|
return {0, sizeof(vertex_p2_s2_st2_col4_a1_u32), VK_VERTEX_INPUT_RATE_VERTEX};
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::array<VkVertexInputAttributeDescription, 6> getAttributeDescriptions() {
|
||||||
|
return {
|
||||||
|
{
|
||||||
|
{0, 0, VK_FORMAT_R32G32_SFLOAT, offsetof(vertex_p2_s2_st2_col4_a1_u32, pos)},
|
||||||
|
{1, 0, VK_FORMAT_R32G32_SFLOAT, offsetof(vertex_p2_s2_st2_col4_a1_u32, scale)},
|
||||||
|
{2, 0, VK_FORMAT_R32G32_SFLOAT, offsetof(vertex_p2_s2_st2_col4_a1_u32, uv)},
|
||||||
|
{3, 0, VK_FORMAT_R32G32B32A32_SFLOAT, offsetof(vertex_p2_s2_st2_col4_a1_u32, color)},
|
||||||
|
{4, 0, VK_FORMAT_R32_SFLOAT, offsetof(vertex_p2_s2_st2_col4_a1_u32, alpha)},
|
||||||
|
{5, 0, VK_FORMAT_R32_UINT, offsetof(vertex_p2_s2_st2_col4_a1_u32, textureID)},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// commands
|
||||||
|
|
||||||
|
enum class PipelineType : uint8_t {
|
||||||
|
None,
|
||||||
|
TexturedQuad,
|
||||||
|
ColoredQuad,
|
||||||
|
Line,
|
||||||
|
Text,
|
||||||
|
Chunk
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TexturedQuadCmd {
|
||||||
|
glm::vec2 position;
|
||||||
|
glm::vec2 size;
|
||||||
|
glm::vec2 uvMin;
|
||||||
|
glm::vec2 uvMax;
|
||||||
|
glm::vec4 color;
|
||||||
|
uint16_t textureID;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ColoredQuadCmd {
|
||||||
|
glm::vec2 pos;
|
||||||
|
glm::vec2 scale;
|
||||||
|
glm::vec4 color;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LineCmd {
|
||||||
|
glm::vec2 start;
|
||||||
|
glm::vec2 end;
|
||||||
|
glm::vec4 color;
|
||||||
|
};
|
||||||
|
|
||||||
|
// struct TextCmd {
|
||||||
|
// Font* font;
|
||||||
|
// std::string text;
|
||||||
|
// glm::vec2 position;
|
||||||
|
// glm::vec4 color;
|
||||||
|
// };
|
||||||
|
|
||||||
|
struct ChunkCmd {
|
||||||
|
VkBuffer vertexBuffer;
|
||||||
|
VkBuffer indexBuffer;
|
||||||
|
uint32_t indexCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SortKey {
|
||||||
|
uint16_t depth; // world Z or Y-sorted depth
|
||||||
|
uint16_t materialID; // texture sheet, font atlas, etc.
|
||||||
|
uint8_t pipeline; // PipelineType
|
||||||
|
|
||||||
|
bool operator<(const SortKey& b) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RenderCommand {
|
||||||
|
|
||||||
|
SortKey key;
|
||||||
|
PipelineType pipeline;
|
||||||
|
|
||||||
|
union {
|
||||||
|
TexturedQuadCmd textured_quad;
|
||||||
|
ColoredQuadCmd colored_quad;
|
||||||
|
LineCmd line;
|
||||||
|
// TextCmd text;
|
||||||
|
ChunkCmd chunk;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
struct AllocatedBuffer {
|
||||||
|
VkBuffer buffer;
|
||||||
|
VmaAllocation allocation;
|
||||||
|
VmaAllocationInfo info;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GPUMeshBuffers {
|
||||||
|
AllocatedBuffer indexBuffer;
|
||||||
|
AllocatedBuffer vertexBuffer;
|
||||||
|
VkDeviceAddress vertexBufferAddress;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Renderer {
|
||||||
|
std::vector<RenderCommand> commands{};
|
||||||
|
|
||||||
|
VkDescriptorSetLayout descriptor_set_layout{};
|
||||||
|
VkPipelineLayout pipelineLayout{};
|
||||||
|
VkPipeline textured_quad_pipeline{};
|
||||||
|
VkPipeline colored_quad_pipeline{};
|
||||||
|
VkPipeline line_pipeline{};
|
||||||
|
VkPipeline text_pipeline{};
|
||||||
|
VkPipeline chunk_pipeline{};
|
||||||
|
VkDescriptorSet set{};
|
||||||
|
|
||||||
|
VkSampler defaultSampler{};
|
||||||
|
|
||||||
|
uint32_t nextTextureSlot = 0;
|
||||||
|
|
||||||
|
struct Frame {
|
||||||
|
VkCommandPool commandPool{};
|
||||||
|
VkCommandBuffer command_buffer{};
|
||||||
|
|
||||||
|
VkSemaphore imageAvailable{};
|
||||||
|
VkFence in_flight_fence{};
|
||||||
|
|
||||||
|
AllocatedBuffer vertexBuffer{};
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<Frame> frames;
|
||||||
|
uint32_t currentFrame = 0;
|
||||||
|
|
||||||
|
VkDescriptorPool descriptorPool{};
|
||||||
|
std::vector<VkDescriptorSet> textureSets{};
|
||||||
|
|
||||||
|
void begin_frame();
|
||||||
|
void end_frame();
|
||||||
|
void flush();
|
||||||
|
|
||||||
|
void submit_sprite(glm::vec2 pos, const sprite_t &sprite);
|
||||||
|
void submit_quad(glm::vec2 pos, glm::vec2 scale);
|
||||||
|
|
||||||
|
explicit Renderer(GLFWwindow *window);
|
||||||
|
void create_pipeline_layout();
|
||||||
|
void createFrameResources();
|
||||||
|
void create_default_sampler();
|
||||||
|
void recordCommandBuffer(
|
||||||
|
VkCommandBuffer cmd,
|
||||||
|
VkImage image,
|
||||||
|
VkImageView imageView,
|
||||||
|
VkExtent2D extent,
|
||||||
|
VkImageLayout oldLayout,
|
||||||
|
const Frame &frame,
|
||||||
|
const std::vector<vertex_p2_s2_st2_col4_a1_u32> &vertices) const;
|
||||||
|
void immediate_submit(std::function<void(VkCommandBuffer)>&& func) const;
|
||||||
|
void transition_image_layout(VkCommandBuffer cmd, VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout) const;
|
||||||
|
VkImageView create_image_view(VkImage image, VkFormat format) const;
|
||||||
|
AllocatedBuffer create_buffer(size_t allocSize, VkBufferUsageFlags usage, VmaMemoryUsage memoryUsage);
|
||||||
|
void destroy_buffer(const AllocatedBuffer& buffer);
|
||||||
|
// GPUMeshBuffers uploadMesh(std::span<uint32_t> indices, std::span<vertex_p2_st2_col4_a1_u32> vertices);
|
||||||
|
void upload_vertex_buffer(
|
||||||
|
VkCommandBuffer cmd,
|
||||||
|
const Frame &frame,
|
||||||
|
std::span<const vertex_p2_s2_st2_col4_a1_u32> vertices) const;
|
||||||
|
|
||||||
|
[[nodiscard]] VkPipeline get_pipeline(PipelineType type) const;
|
||||||
|
// void bind_material(VkCommandBuffer cmd, uint16_t materialID);
|
||||||
|
void create_descriptor_pool();
|
||||||
|
void update_bindless_slot(uint32_t slot, VkImageView view, VkSampler sampler) const;
|
||||||
|
|
||||||
|
// Returns the resource info so the Manager can store it
|
||||||
|
void upload_texture(
|
||||||
|
int w,
|
||||||
|
int h,
|
||||||
|
const void* pixels,
|
||||||
|
VkImage *image,
|
||||||
|
VmaAllocation *allocation,
|
||||||
|
VkImageView *view,
|
||||||
|
uint32_t *descriptor_index);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
VkPipeline create_graphics_pipeline(
|
||||||
|
VkDevice device,
|
||||||
|
VkPipelineLayout layout,
|
||||||
|
VkFormat colorFormat,
|
||||||
|
// VkShaderModule vertShader,
|
||||||
|
// VkShaderModule fragShader,
|
||||||
|
VkPrimitiveTopology topology,
|
||||||
|
bool enableBlending)
|
||||||
|
{
|
||||||
|
|
||||||
|
auto slangTargets{ std::to_array<slang::TargetDesc>({ {
|
||||||
|
.format = SLANG_SPIRV,
|
||||||
|
.profile = slangGlobalSession->findProfile("spirv_1_4")
|
||||||
|
} })};
|
||||||
|
auto slangOptions{ std::to_array<slang::CompilerOptionEntry>({ {
|
||||||
|
slang::CompilerOptionName::EmitSpirvDirectly,
|
||||||
|
{slang::CompilerOptionValueKind::Int, 1}
|
||||||
|
} })};
|
||||||
|
slang::SessionDesc slangSessionDesc{
|
||||||
|
.targets = slangTargets.data(),
|
||||||
|
.targetCount = SlangInt(slangTargets.size()),
|
||||||
|
.defaultMatrixLayoutMode = SLANG_MATRIX_LAYOUT_COLUMN_MAJOR,
|
||||||
|
.compilerOptionEntries = slangOptions.data(),
|
||||||
|
.compilerOptionEntryCount = uint32_t(slangOptions.size())
|
||||||
|
};
|
||||||
|
Slang::ComPtr<slang::ISession> slangSession;
|
||||||
|
slangGlobalSession->createSession(slangSessionDesc, slangSession.writeRef());
|
||||||
|
|
||||||
|
Slang::ComPtr<slang::IModule> slangModule{
|
||||||
|
slangSession->loadModuleFromSource("triangle", "shaders/shader.slang", nullptr, nullptr)
|
||||||
|
};
|
||||||
|
Slang::ComPtr<ISlangBlob> spirv;
|
||||||
|
slangModule->getTargetCode(0, spirv.writeRef());
|
||||||
|
|
||||||
|
VkShaderModuleCreateInfo shaderModuleCI{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
|
||||||
|
.codeSize = spirv->getBufferSize(),
|
||||||
|
.pCode = (uint32_t*)spirv->getBufferPointer()
|
||||||
|
};
|
||||||
|
VkShaderModule shaderModule{};
|
||||||
|
vkCreateShaderModule(device, &shaderModuleCI, nullptr, &shaderModule);
|
||||||
|
|
||||||
|
auto vsCode = loadFile("shaders/triangle.vert.spv");
|
||||||
|
auto fsCode = loadFile("shaders/triangle.frag.spv");
|
||||||
|
|
||||||
|
VkShaderModuleCreateInfo smci{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO
|
||||||
|
};
|
||||||
|
|
||||||
|
smci.codeSize = vsCode.size();
|
||||||
|
smci.pCode = reinterpret_cast<uint32_t*>(vsCode.data());
|
||||||
|
// VkShaderModule vs;
|
||||||
|
// vkCreateShaderModule(device, &smci, nullptr, &vs);
|
||||||
|
|
||||||
|
smci.codeSize = fsCode.size();
|
||||||
|
smci.pCode = reinterpret_cast<uint32_t*>(fsCode.data());
|
||||||
|
// VkShaderModule fs;
|
||||||
|
// vkCreateShaderModule(device, &smci, nullptr, &fs);
|
||||||
|
|
||||||
|
// --- Shaders ---
|
||||||
|
std::vector<VkPipelineShaderStageCreateInfo> shaderStages{
|
||||||
|
{ .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||||||
|
.stage = VK_SHADER_STAGE_VERTEX_BIT,
|
||||||
|
.module = shaderModule, .pName = "main"},
|
||||||
|
{ .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||||||
|
.stage = VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||||
|
.module = shaderModule, .pName = "main" }
|
||||||
|
};
|
||||||
|
|
||||||
|
// --- Vertex Input (Generic) ---
|
||||||
|
auto binding = T::getBindingDescription();
|
||||||
|
auto attrs = T::getAttributeDescriptions();
|
||||||
|
|
||||||
|
// --- Vertex Input (Matching our vertex_p2_st2_col4 struct) ---
|
||||||
|
VkPipelineVertexInputStateCreateInfo vi{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
||||||
|
.vertexBindingDescriptionCount = 1,
|
||||||
|
.pVertexBindingDescriptions = &binding,
|
||||||
|
.vertexAttributeDescriptionCount = attrs.size(),
|
||||||
|
.pVertexAttributeDescriptions = attrs.data(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// --- Input Assembly (Changes based on Topology parameter) ---
|
||||||
|
VkPipelineInputAssemblyStateCreateInfo ia{VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO};
|
||||||
|
ia.topology = topology;
|
||||||
|
|
||||||
|
// --- Blending (Changes based on enableBlending parameter) ---
|
||||||
|
VkPipelineColorBlendAttachmentState colorBlend{
|
||||||
|
.blendEnable = enableBlending ? VK_TRUE : VK_FALSE,
|
||||||
|
.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA,
|
||||||
|
.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
|
||||||
|
.colorBlendOp = VK_BLEND_OP_ADD,
|
||||||
|
.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
|
||||||
|
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
|
||||||
|
.alphaBlendOp = VK_BLEND_OP_ADD,
|
||||||
|
.colorWriteMask = 0xF
|
||||||
|
};
|
||||||
|
|
||||||
|
// --- Boilerplate (Standard 2D Defaults) ---
|
||||||
|
VkPipelineViewportStateCreateInfo vp{
|
||||||
|
VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
|
||||||
|
nullptr,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
nullptr,
|
||||||
|
1,
|
||||||
|
nullptr
|
||||||
|
};
|
||||||
|
|
||||||
|
VkPipelineRasterizationStateCreateInfo rs{
|
||||||
|
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
|
||||||
|
nullptr,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
VK_POLYGON_MODE_FILL,
|
||||||
|
VK_CULL_MODE_NONE,
|
||||||
|
VK_FRONT_FACE_COUNTER_CLOCKWISE,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1.0f
|
||||||
|
};
|
||||||
|
|
||||||
|
VkPipelineMultisampleStateCreateInfo ms{
|
||||||
|
VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
||||||
|
nullptr,
|
||||||
|
0,
|
||||||
|
VK_SAMPLE_COUNT_1_BIT
|
||||||
|
};
|
||||||
|
|
||||||
|
VkPipelineColorBlendStateCreateInfo cb{
|
||||||
|
VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
|
||||||
|
nullptr,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
VK_LOGIC_OP_AND,
|
||||||
|
1,
|
||||||
|
&colorBlend
|
||||||
|
};
|
||||||
|
|
||||||
|
VkDynamicState dyns[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
|
||||||
|
VkPipelineDynamicStateCreateInfo ds{
|
||||||
|
VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
|
||||||
|
nullptr,
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
dyns
|
||||||
|
};
|
||||||
|
|
||||||
|
VkPipelineRenderingCreateInfo rci{
|
||||||
|
VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO,
|
||||||
|
nullptr,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
&colorFormat
|
||||||
|
};
|
||||||
|
|
||||||
|
VkGraphicsPipelineCreateInfo gpci{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
|
||||||
|
.pNext = &rci,
|
||||||
|
.stageCount = (uint32_t) shaderStages.size(),
|
||||||
|
.pStages = shaderStages.data(),
|
||||||
|
.pVertexInputState = &vi,
|
||||||
|
.pInputAssemblyState = &ia,
|
||||||
|
.pViewportState = &vp,
|
||||||
|
.pRasterizationState = &rs,
|
||||||
|
.pMultisampleState = &ms,
|
||||||
|
.pColorBlendState = &cb,
|
||||||
|
.pDynamicState = &ds,
|
||||||
|
.layout = layout
|
||||||
|
};
|
||||||
|
|
||||||
|
VkPipeline pipeline;
|
||||||
|
vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &gpci, nullptr, &pipeline);
|
||||||
|
return pipeline;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //V_RENDERER_H
|
||||||
8
renderer/metal/vertex_data.h
Normal file
8
renderer/metal/vertex_data.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
//
|
||||||
|
// Created by Vicente Ferrari Smith on 27.02.26.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef V_VERTEX_DATA_H
|
||||||
|
#define V_VERTEX_DATA_H
|
||||||
|
|
||||||
|
#endif //V_VERTEX_DATA_H
|
||||||
@ -2,4 +2,4 @@
|
|||||||
// Created by Vicente Ferrari Smith on 14.02.26.
|
// Created by Vicente Ferrari Smith on 14.02.26.
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "sprite.h"
|
#include "../sprite.h"
|
||||||
|
|||||||
@ -1,38 +1,3 @@
|
|||||||
//
|
//
|
||||||
// Created by Vicente Ferrari Smith on 14.02.26.
|
// Created by Vicente Ferrari Smith on 01.03.26.
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "texture.h"
|
|
||||||
#include <stb_image.h>
|
|
||||||
#include "renderer.h"
|
|
||||||
|
|
||||||
TextureManager::TextureManager() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Texture TextureManager::load(const std::string& path, Renderer &renderer) {
|
|
||||||
// Dedup: Don't load the same file twice!
|
|
||||||
// if (path_to_id.contains(path)) return path_to_id[path];
|
|
||||||
|
|
||||||
int w, h, ch;
|
|
||||||
unsigned char* data = stbi_load(path.c_str(), &w, &h, &ch, STBI_rgb_alpha);
|
|
||||||
|
|
||||||
// Tell the renderer to make the GPU version
|
|
||||||
Texture res;
|
|
||||||
res.width = w;
|
|
||||||
res.height = h;
|
|
||||||
res.channels = STBI_rgb_alpha;
|
|
||||||
res.srgb = true;
|
|
||||||
renderer.upload_texture(w, h, data, &res.image, &res.allocation, &res.view, &res.descriptor_index);
|
|
||||||
|
|
||||||
stbi_image_free(data);
|
|
||||||
|
|
||||||
res.id = path;
|
|
||||||
res.path = path;
|
|
||||||
res.uploaded = true;
|
|
||||||
|
|
||||||
textures[path] = res;
|
|
||||||
// path_to_id[path] = id;
|
|
||||||
|
|
||||||
return res; // This is the textureID for your sprites
|
|
||||||
}
|
|
||||||
|
|||||||
@ -2,4 +2,4 @@
|
|||||||
// Created by Vicente Ferrari Smith on 14.02.26.
|
// Created by Vicente Ferrari Smith on 14.02.26.
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "texture_sheet.h"
|
#include "../texture_sheet.h"
|
||||||
|
|||||||
828
renderer/vulkan/renderer.cpp
Normal file
828
renderer/vulkan/renderer.cpp
Normal file
@ -0,0 +1,828 @@
|
|||||||
|
//
|
||||||
|
// Created by Vicente Ferrari Smith on 13.02.26.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "../Grpahics.h"
|
||||||
|
|
||||||
|
#include <print>
|
||||||
|
|
||||||
|
#include "init.h"
|
||||||
|
#include "../sprite.h"
|
||||||
|
#include <vma/vk_mem_alloc.h>
|
||||||
|
#include <slang/slang.h>
|
||||||
|
|
||||||
|
extern int32_t window_width;
|
||||||
|
extern int32_t window_height;
|
||||||
|
|
||||||
|
bool SortKey::operator<(const SortKey& b) const {
|
||||||
|
if (depth != b.depth) return depth < b.depth;
|
||||||
|
if (pipeline != b.pipeline) return pipeline < b.pipeline;
|
||||||
|
return materialID < b.materialID;
|
||||||
|
}
|
||||||
|
|
||||||
|
Renderer::Renderer(GLFWwindow *window) {
|
||||||
|
|
||||||
|
create_pipeline_layout();
|
||||||
|
colored_quad_pipeline = create_graphics_pipeline<vertex_p2_s2_st2_col4_a1_u32>(
|
||||||
|
device,
|
||||||
|
pipelineLayout,
|
||||||
|
swapchain_format.format,
|
||||||
|
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
create_default_sampler();
|
||||||
|
create_descriptor_pool();
|
||||||
|
createFrameResources();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::begin_frame() {
|
||||||
|
commands.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::flush() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::submit_quad(glm::vec2 pos, glm::vec2 scale) {
|
||||||
|
RenderCommand cmd {};
|
||||||
|
cmd.pipeline = PipelineType::ColoredQuad;
|
||||||
|
cmd.key = {
|
||||||
|
(uint16_t) pos.y,
|
||||||
|
0,
|
||||||
|
(uint8_t) PipelineType::ColoredQuad
|
||||||
|
};
|
||||||
|
|
||||||
|
cmd.colored_quad = {
|
||||||
|
.pos = pos,
|
||||||
|
.scale = scale,
|
||||||
|
.color = {0, 1, 1, 1},
|
||||||
|
};
|
||||||
|
|
||||||
|
commands.push_back(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::submit_sprite(glm::vec2 pos, const sprite_t &sprite) {
|
||||||
|
RenderCommand cmd {};
|
||||||
|
cmd.pipeline = PipelineType::TexturedQuad;
|
||||||
|
cmd.key = {
|
||||||
|
(uint16_t) pos.y,
|
||||||
|
0,
|
||||||
|
(uint8_t) PipelineType::TexturedQuad
|
||||||
|
};
|
||||||
|
|
||||||
|
cmd.textured_quad = {
|
||||||
|
.position = pos,
|
||||||
|
.size = {0, 0},
|
||||||
|
.uvMin = {0, 0},
|
||||||
|
.uvMax = {0, 0},
|
||||||
|
.color = {1, 1, 1, 1},
|
||||||
|
.textureID = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
commands.push_back(cmd);
|
||||||
|
|
||||||
|
// assert(started == true, "You can't submit without having started the renderer first.");
|
||||||
|
// renderable : Renderable;
|
||||||
|
// renderable.type = .Sprite;
|
||||||
|
//
|
||||||
|
// if sprite.window_space
|
||||||
|
// renderable.projection_type = .ORTHOGRAPHIC_WINDOW;
|
||||||
|
// else
|
||||||
|
// renderable.projection_type = .ORTHOGRAPHIC_WORLD;
|
||||||
|
//
|
||||||
|
// renderable.pos = pos;
|
||||||
|
// renderable.sprite.texture_sheet = sprite.texture_sheet;
|
||||||
|
// renderable.sprite.texture_cell = sprite.texture_cell;
|
||||||
|
// renderable.sprite.origin = sprite.origin;
|
||||||
|
// renderable.sprite.scale = sprite.scale;
|
||||||
|
// renderable.sprite.colour = sprite.colour;
|
||||||
|
// renderable.sprite.alpha = alpha;
|
||||||
|
//
|
||||||
|
// array_add(*renderer.renderable_list, renderable);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::create_pipeline_layout() {
|
||||||
|
std::array<VkDescriptorSetLayoutBinding, 1> bindings = {
|
||||||
|
{
|
||||||
|
{
|
||||||
|
.binding = 0,
|
||||||
|
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||||
|
.descriptorCount = nextTextureSlot,
|
||||||
|
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
VkDescriptorBindingFlags flags[1] = {
|
||||||
|
VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT | VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT
|
||||||
|
};
|
||||||
|
|
||||||
|
VkDescriptorSetLayoutBindingFlagsCreateInfo layoutFlags{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO,
|
||||||
|
.bindingCount = 1,
|
||||||
|
.pBindingFlags = flags
|
||||||
|
};
|
||||||
|
|
||||||
|
VkDescriptorSetLayoutCreateInfo dslci{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
|
||||||
|
.pNext = &layoutFlags,
|
||||||
|
// .flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT,
|
||||||
|
.bindingCount = bindings.size(),
|
||||||
|
.pBindings = bindings.data()
|
||||||
|
};
|
||||||
|
|
||||||
|
vkCreateDescriptorSetLayout(device, &dslci, nullptr, &descriptor_set_layout);
|
||||||
|
|
||||||
|
VkPushConstantRange push_constant{
|
||||||
|
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
|
||||||
|
.offset = 0,
|
||||||
|
.size = sizeof(glm::mat4),
|
||||||
|
};
|
||||||
|
|
||||||
|
VkPipelineLayoutCreateInfo plci{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
|
||||||
|
.pNext = nullptr,
|
||||||
|
.flags = 0,
|
||||||
|
.setLayoutCount = 1,
|
||||||
|
.pSetLayouts = &descriptor_set_layout,
|
||||||
|
.pushConstantRangeCount = 1,
|
||||||
|
.pPushConstantRanges = &push_constant,
|
||||||
|
};
|
||||||
|
|
||||||
|
vkCreatePipelineLayout(device, &plci, nullptr, &pipelineLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::createFrameResources() {
|
||||||
|
|
||||||
|
const VkSemaphoreCreateInfo seci{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
|
||||||
|
};
|
||||||
|
|
||||||
|
VkFenceCreateInfo fenceInfo{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
|
||||||
|
.flags = VK_FENCE_CREATE_SIGNALED_BIT,
|
||||||
|
};
|
||||||
|
|
||||||
|
VkCommandPoolCreateInfo cpci{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
|
||||||
|
.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
|
||||||
|
.queueFamilyIndex = queueFamily
|
||||||
|
};
|
||||||
|
|
||||||
|
frames.resize(MAX_FRAMES_IN_FLIGHT);
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) {
|
||||||
|
Frame &frame = frames[i];
|
||||||
|
|
||||||
|
vkCreateSemaphore(device, &seci, nullptr, &frame.imageAvailable);
|
||||||
|
|
||||||
|
vkCreateFence(device, &fenceInfo, nullptr, &frame.in_flight_fence);
|
||||||
|
|
||||||
|
|
||||||
|
vkCreateCommandPool(device, &cpci, nullptr, &frame.commandPool);
|
||||||
|
|
||||||
|
const VkCommandBufferAllocateInfo cbai{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
|
||||||
|
.commandPool = frame.commandPool,
|
||||||
|
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
|
||||||
|
.commandBufferCount = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
vkAllocateCommandBuffers(device, &cbai, &frame.command_buffer);
|
||||||
|
|
||||||
|
VkBufferCreateInfo bufferInfo = {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||||||
|
.size = 1024 * 1024 * 4,
|
||||||
|
.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
|
||||||
|
};
|
||||||
|
|
||||||
|
VmaAllocationCreateInfo allocCreateInfo = {};
|
||||||
|
allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
|
||||||
|
allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
|
||||||
|
VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT |
|
||||||
|
VMA_ALLOCATION_CREATE_MAPPED_BIT;
|
||||||
|
|
||||||
|
vmaCreateBuffer(
|
||||||
|
allocator,
|
||||||
|
&bufferInfo,
|
||||||
|
&allocCreateInfo,
|
||||||
|
&frame.vertexBuffer.buffer,
|
||||||
|
&frame.vertexBuffer.allocation,
|
||||||
|
&frame.vertexBuffer.info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AllocatedBuffer Renderer::create_buffer(size_t allocSize, VkBufferUsageFlags usage, VmaMemoryUsage memoryUsage) {
|
||||||
|
// allocate buffer
|
||||||
|
VkBufferCreateInfo bufferInfo = {.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO};
|
||||||
|
bufferInfo.pNext = nullptr;
|
||||||
|
bufferInfo.size = allocSize;
|
||||||
|
|
||||||
|
bufferInfo.usage = usage;
|
||||||
|
|
||||||
|
VmaAllocationCreateInfo vmaallocInfo = {};
|
||||||
|
vmaallocInfo.usage = memoryUsage;
|
||||||
|
vmaallocInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
|
||||||
|
AllocatedBuffer newBuffer{};
|
||||||
|
|
||||||
|
// allocate the buffer
|
||||||
|
vmaCreateBuffer(allocator, &bufferInfo, &vmaallocInfo, &newBuffer.buffer, &newBuffer.allocation, &newBuffer.info);
|
||||||
|
|
||||||
|
return newBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::destroy_buffer(const AllocatedBuffer& buffer) {
|
||||||
|
vmaDestroyBuffer(allocator, buffer.buffer, buffer.allocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
// GPUMeshBuffers Renderer::uploadMesh(std::span<uint32_t> indices, std::span<vertex_p2_st2_col4_a1_u32> vertices) {
|
||||||
|
// const size_t vertexBufferSize = vertices.size() * sizeof(vertex_p2_st2_col4_a1_u32);
|
||||||
|
// const size_t indexBufferSize = indices.size() * sizeof(uint32_t);
|
||||||
|
//
|
||||||
|
// GPUMeshBuffers newSurface;
|
||||||
|
//
|
||||||
|
// //create vertex buffer
|
||||||
|
// newSurface.vertexBuffer = create_buffer(vertexBufferSize, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
|
||||||
|
// VMA_MEMORY_USAGE_GPU_ONLY);
|
||||||
|
//
|
||||||
|
// //find the adress of the vertex buffer
|
||||||
|
// VkBufferDeviceAddressInfo deviceAdressInfo{ .sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO,.buffer = newSurface.vertexBuffer.buffer };
|
||||||
|
// newSurface.vertexBufferAddress = vkGetBufferDeviceAddress(device, &deviceAdressInfo);
|
||||||
|
//
|
||||||
|
// //create index buffer
|
||||||
|
// newSurface.indexBuffer = create_buffer(indexBufferSize, VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
|
||||||
|
// VMA_MEMORY_USAGE_GPU_ONLY);
|
||||||
|
//
|
||||||
|
// AllocatedBuffer staging = create_buffer(vertexBufferSize + indexBufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_ONLY);
|
||||||
|
//
|
||||||
|
// void* data = staging.allocation->GetMappedData();
|
||||||
|
//
|
||||||
|
// // copy vertex buffer
|
||||||
|
// memcpy(data, vertices.data(), vertexBufferSize);
|
||||||
|
// // copy index buffer
|
||||||
|
// memcpy((char*)data + vertexBufferSize, indices.data(), indexBufferSize);
|
||||||
|
//
|
||||||
|
// immediate_submit([&](VkCommandBuffer cmd) {
|
||||||
|
// VkBufferCopy vertexCopy{ 0 };
|
||||||
|
// vertexCopy.dstOffset = 0;
|
||||||
|
// vertexCopy.srcOffset = 0;
|
||||||
|
// vertexCopy.size = vertexBufferSize;
|
||||||
|
//
|
||||||
|
// vkCmdCopyBuffer(cmd, staging.buffer, newSurface.vertexBuffer.buffer, 1, &vertexCopy);
|
||||||
|
//
|
||||||
|
// VkBufferCopy indexCopy{ 0 };
|
||||||
|
// indexCopy.dstOffset = 0;
|
||||||
|
// indexCopy.srcOffset = vertexBufferSize;
|
||||||
|
// indexCopy.size = indexBufferSize;
|
||||||
|
//
|
||||||
|
// vkCmdCopyBuffer(cmd, staging.buffer, newSurface.indexBuffer.buffer, 1, &indexCopy);
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// destroy_buffer(staging);
|
||||||
|
//
|
||||||
|
// return newSurface;
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
|
||||||
|
VkPipeline Renderer::get_pipeline(PipelineType type) const {
|
||||||
|
switch (type) {
|
||||||
|
case PipelineType::TexturedQuad: return textured_quad_pipeline;
|
||||||
|
case PipelineType::ColoredQuad: return colored_quad_pipeline;
|
||||||
|
case PipelineType::Line: return line_pipeline;
|
||||||
|
default: return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// void Renderer::bind_material(VkCommandBuffer cmd, uint16_t materialID) {
|
||||||
|
// // In a real app, you'd have an array/map: std::vector<VkDescriptorSet> textureSets;
|
||||||
|
// VkDescriptorSet set = textureSets[materialID];
|
||||||
|
//
|
||||||
|
// vkCmdBindDescriptorSets(
|
||||||
|
// cmd,
|
||||||
|
// VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||||
|
// pipelineLayout, // Our shared layout
|
||||||
|
// 0, // Starting at Set 0
|
||||||
|
// 1, // Binding 1 set
|
||||||
|
// &set,
|
||||||
|
// 0, nullptr
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
void Renderer::create_descriptor_pool() {
|
||||||
|
VkDescriptorPoolSize pool_sizes[] = {
|
||||||
|
{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, nextTextureSlot },
|
||||||
|
};
|
||||||
|
|
||||||
|
VkDescriptorPoolCreateInfo pool_info{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
|
||||||
|
.flags = VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT,
|
||||||
|
.maxSets = 1,
|
||||||
|
.poolSizeCount = 1,
|
||||||
|
.pPoolSizes = pool_sizes
|
||||||
|
};
|
||||||
|
|
||||||
|
vkCreateDescriptorPool(device, &pool_info, nullptr, &descriptorPool);
|
||||||
|
|
||||||
|
VkDescriptorSetAllocateInfo alloc_info{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
|
||||||
|
.descriptorPool = descriptorPool,
|
||||||
|
.descriptorSetCount = 1,
|
||||||
|
.pSetLayouts = &descriptor_set_layout
|
||||||
|
};
|
||||||
|
|
||||||
|
vkAllocateDescriptorSets(device, &alloc_info, &set);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::update_bindless_slot(uint32_t slot, VkImageView view, VkSampler sampler) const {
|
||||||
|
VkDescriptorImageInfo image_info{
|
||||||
|
.sampler = sampler,
|
||||||
|
.imageView = view,
|
||||||
|
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
|
||||||
|
};
|
||||||
|
|
||||||
|
VkWriteDescriptorSet write{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||||
|
.dstSet = set,
|
||||||
|
.dstArrayElement = slot,
|
||||||
|
.descriptorCount = 1,
|
||||||
|
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||||
|
.pImageInfo = &image_info
|
||||||
|
};
|
||||||
|
|
||||||
|
vkUpdateDescriptorSets(device, 1, &write, 0, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::upload_texture(
|
||||||
|
const int w,
|
||||||
|
const int h,
|
||||||
|
const void* pixels,
|
||||||
|
VkImage *image,
|
||||||
|
VmaAllocation *allocation,
|
||||||
|
VkImageView *view,
|
||||||
|
uint32_t *descriptor_index)
|
||||||
|
{
|
||||||
|
VkDeviceSize imageSize = w * h * 4;
|
||||||
|
|
||||||
|
// --- 1. Create Staging Buffer (CPU Visible) ---
|
||||||
|
VkBuffer stagingBuffer;
|
||||||
|
VmaAllocation stagingAlloc;
|
||||||
|
|
||||||
|
VkBufferCreateInfo stagingBufferInfo = { .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
|
||||||
|
stagingBufferInfo.size = imageSize;
|
||||||
|
stagingBufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
|
||||||
|
|
||||||
|
VmaAllocationCreateInfo stagingAllocCreateInfo = {
|
||||||
|
.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT,
|
||||||
|
.usage = VMA_MEMORY_USAGE_AUTO,
|
||||||
|
};
|
||||||
|
|
||||||
|
VmaAllocationInfo stagingResultInfo;
|
||||||
|
vmaCreateBuffer(allocator, &stagingBufferInfo, &stagingAllocCreateInfo, &stagingBuffer, &stagingAlloc, &stagingResultInfo);
|
||||||
|
|
||||||
|
// Copy raw pixels into the mapped memory provided by VMA
|
||||||
|
memcpy(stagingResultInfo.pMappedData, pixels, imageSize);
|
||||||
|
|
||||||
|
// --- 2. Create GPU Image (Device Local / Tiled) ---
|
||||||
|
VkExtent3D imageExtent = { (uint32_t) w, (uint32_t) h, 1 };
|
||||||
|
|
||||||
|
VkImageCreateInfo imageInfo = {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||||||
|
.imageType = VK_IMAGE_TYPE_2D,
|
||||||
|
.format = VK_FORMAT_R8G8B8A8_UNORM,
|
||||||
|
.extent = imageExtent,
|
||||||
|
.mipLevels = 1,
|
||||||
|
.arrayLayers = 1,
|
||||||
|
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||||||
|
.tiling = VK_IMAGE_TILING_OPTIMAL,
|
||||||
|
.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
|
||||||
|
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED
|
||||||
|
};
|
||||||
|
|
||||||
|
VmaAllocationCreateInfo imageAllocCreateInfo = {
|
||||||
|
.usage = VMA_MEMORY_USAGE_AUTO,
|
||||||
|
.priority = 1.0f,
|
||||||
|
};
|
||||||
|
|
||||||
|
vmaCreateImage(allocator, &imageInfo, &imageAllocCreateInfo, image, allocation, nullptr);
|
||||||
|
|
||||||
|
// --- 3. The Transfer ---
|
||||||
|
immediate_submit([&](VkCommandBuffer cmd) {
|
||||||
|
// Transition image from UNDEFINED to TRANSFER_DST
|
||||||
|
transition_image_layout(cmd, *image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||||
|
|
||||||
|
VkBufferImageCopy copyRegion = {};
|
||||||
|
copyRegion.bufferOffset = 0;
|
||||||
|
copyRegion.bufferRowLength = 0;
|
||||||
|
copyRegion.bufferImageHeight = 0;
|
||||||
|
copyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
|
copyRegion.imageSubresource.mipLevel = 0;
|
||||||
|
copyRegion.imageSubresource.baseArrayLayer = 0;
|
||||||
|
copyRegion.imageSubresource.layerCount = 1;
|
||||||
|
copyRegion.imageExtent = imageExtent;
|
||||||
|
|
||||||
|
vkCmdCopyBufferToImage(cmd, stagingBuffer, *image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©Region);
|
||||||
|
|
||||||
|
// Transition image from TRANSFER_DST to SHADER_READ_ONLY
|
||||||
|
transition_image_layout(cmd, *image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Clean up temporary staging resources
|
||||||
|
vmaDestroyBuffer(allocator, stagingBuffer, stagingAlloc);
|
||||||
|
|
||||||
|
// --- 4. Finalize Handles ---
|
||||||
|
*view = create_image_view(*image, imageInfo.format);
|
||||||
|
|
||||||
|
// Register in your Bindless Array (Set 0, Binding 0, Index N)
|
||||||
|
*descriptor_index = nextTextureSlot++;
|
||||||
|
update_bindless_slot(*descriptor_index, *view, defaultSampler);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::immediate_submit(std::function<void(VkCommandBuffer)>&& func) const {
|
||||||
|
VkCommandBufferAllocateInfo allocInfo{ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
|
||||||
|
allocInfo.commandPool = frames[currentFrame].commandPool; // Use a pool created with VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT
|
||||||
|
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||||||
|
allocInfo.commandBufferCount = 1;
|
||||||
|
|
||||||
|
VkCommandBuffer cmd;
|
||||||
|
vkAllocateCommandBuffers(device, &allocInfo, &cmd);
|
||||||
|
|
||||||
|
VkCommandBufferBeginInfo beginInfo{ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
|
||||||
|
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
|
||||||
|
|
||||||
|
vkBeginCommandBuffer(cmd, &beginInfo);
|
||||||
|
|
||||||
|
// Execute the code passed in the lambda
|
||||||
|
func(cmd);
|
||||||
|
|
||||||
|
vkEndCommandBuffer(cmd);
|
||||||
|
|
||||||
|
VkSubmitInfo submit{ .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO };
|
||||||
|
submit.commandBufferCount = 1;
|
||||||
|
submit.pCommandBuffers = &cmd;
|
||||||
|
|
||||||
|
// Submit and wait
|
||||||
|
vkQueueSubmit(graphics_queue, 1, &submit, VK_NULL_HANDLE);
|
||||||
|
vkQueueWaitIdle(graphics_queue);
|
||||||
|
|
||||||
|
vkFreeCommandBuffers(device, frames[currentFrame].commandPool, 1, &cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::transition_image_layout(VkCommandBuffer cmd, VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout) const {
|
||||||
|
VkImageMemoryBarrier2 barrier{ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2 };
|
||||||
|
|
||||||
|
barrier.oldLayout = oldLayout;
|
||||||
|
barrier.newLayout = newLayout;
|
||||||
|
barrier.image = image;
|
||||||
|
barrier.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
|
||||||
|
|
||||||
|
// Simple synchronization: wait for all previous commands, and block all future ones
|
||||||
|
// You can optimize these masks later, but this is safe for a 2D engine
|
||||||
|
barrier.srcStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT;
|
||||||
|
barrier.srcAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT;
|
||||||
|
barrier.dstStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT;
|
||||||
|
barrier.dstAccessMask = VK_ACCESS_2_MEMORY_READ_BIT | VK_ACCESS_2_MEMORY_WRITE_BIT;
|
||||||
|
|
||||||
|
VkDependencyInfo dep{ .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO };
|
||||||
|
dep.imageMemoryBarrierCount = 1;
|
||||||
|
dep.pImageMemoryBarriers = &barrier;
|
||||||
|
|
||||||
|
vkCmdPipelineBarrier2(cmd, &dep);
|
||||||
|
}
|
||||||
|
|
||||||
|
VkImageView Renderer::create_image_view(VkImage image, VkFormat format) const {
|
||||||
|
VkImageViewCreateInfo viewInfo{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||||
|
.image = image,
|
||||||
|
.viewType = VK_IMAGE_VIEW_TYPE_2D,
|
||||||
|
.format = format,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Default component mapping (R,G,B,A)
|
||||||
|
viewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||||
|
viewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||||
|
viewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||||
|
viewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||||
|
|
||||||
|
// Which part of the image to look at (Mip 0, Layer 0)
|
||||||
|
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
|
viewInfo.subresourceRange.baseMipLevel = 0;
|
||||||
|
viewInfo.subresourceRange.levelCount = 1;
|
||||||
|
viewInfo.subresourceRange.baseArrayLayer = 0;
|
||||||
|
viewInfo.subresourceRange.layerCount = 1;
|
||||||
|
|
||||||
|
VkImageView view;
|
||||||
|
vkCreateImageView(device, &viewInfo, nullptr, &view);
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::create_default_sampler() {
|
||||||
|
VkSamplerCreateInfo samplerInfo{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
||||||
|
// For crisp pixel art, use NEAREST. For smooth textures, use LINEAR.
|
||||||
|
.magFilter = VK_FILTER_NEAREST,
|
||||||
|
.minFilter = VK_FILTER_NEAREST,
|
||||||
|
|
||||||
|
.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST,
|
||||||
|
|
||||||
|
// How to handle "out of bounds" UVs
|
||||||
|
.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
|
||||||
|
.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
|
||||||
|
.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
|
||||||
|
|
||||||
|
// Optimization: turn off things we don't need for simple 2D
|
||||||
|
.anisotropyEnable = VK_FALSE,
|
||||||
|
.maxAnisotropy = 1.0f,
|
||||||
|
.compareEnable = VK_FALSE,
|
||||||
|
.compareOp = VK_COMPARE_OP_ALWAYS,
|
||||||
|
.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK,
|
||||||
|
.unnormalizedCoordinates = VK_FALSE,
|
||||||
|
};
|
||||||
|
|
||||||
|
vkCreateSampler(device, &samplerInfo, nullptr, &defaultSampler);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::end_frame() {
|
||||||
|
|
||||||
|
Frame &frame = frames[currentFrame];
|
||||||
|
|
||||||
|
vkWaitForFences(device, 1, &frame.in_flight_fence, VK_TRUE, UINT64_MAX);
|
||||||
|
vkResetFences(device, 1, &frame.in_flight_fence);
|
||||||
|
|
||||||
|
uint32_t imageIndex;
|
||||||
|
vkAcquireNextImageKHR(
|
||||||
|
device,
|
||||||
|
swapchain,
|
||||||
|
UINT64_MAX,
|
||||||
|
frame.imageAvailable,
|
||||||
|
VK_NULL_HANDLE,
|
||||||
|
&imageIndex
|
||||||
|
);
|
||||||
|
|
||||||
|
commands = counting_sort_descending(commands, [](const RenderCommand &cmd){
|
||||||
|
return cmd.key.depth;
|
||||||
|
});
|
||||||
|
|
||||||
|
std::vector<vertex_p2_s2_st2_col4_a1_u32> vertices;
|
||||||
|
|
||||||
|
for (auto& cmd : commands) {
|
||||||
|
|
||||||
|
|
||||||
|
switch (cmd.pipeline) {
|
||||||
|
case PipelineType::ColoredQuad: {
|
||||||
|
const auto &q = cmd.colored_quad;
|
||||||
|
|
||||||
|
// Calculate spatial corners
|
||||||
|
//float x0 = q.position.x;
|
||||||
|
//float y0 = q.position.y;
|
||||||
|
//float x1 = q.position.x + q.size.x;
|
||||||
|
//float y1 = q.position.y + q.size.y;
|
||||||
|
|
||||||
|
// Calculate UV corners
|
||||||
|
// float u0 = q.uvMin.x;
|
||||||
|
// float v0 = q.uvMin.y;
|
||||||
|
// float u1 = q.uvMax.x;
|
||||||
|
// float v1 = q.uvMax.y;
|
||||||
|
|
||||||
|
// Define the 4 corners of the quad
|
||||||
|
vertex_p2_s2_st2_col4_a1_u32 vTL = { q.pos, q.scale, {0, 0}, {1, 0, 0, 0}, 1, 0 };
|
||||||
|
vertex_p2_s2_st2_col4_a1_u32 vTR = { q.pos, q.scale, {0, 0}, q.color, 1, 0 };
|
||||||
|
vertex_p2_s2_st2_col4_a1_u32 vBL = { q.pos, q.scale, {0, 0}, q.color, 1, 0 };
|
||||||
|
vertex_p2_s2_st2_col4_a1_u32 vBR = { q.pos, q.scale, {0, 0}, q.color, 1, 0 };
|
||||||
|
|
||||||
|
// vertex_p2_st2_col4_a1_u32 vTL = { {x0, y0}, {u0, v0}, q.color, 1, q.textureID };
|
||||||
|
// vertex_p2_st2_col4_a1_u32 vTR = { {x1, y0}, {u1, v0}, q.color, 1, q.textureID };
|
||||||
|
// vertex_p2_st2_col4_a1_u32 vBL = { {x0, y1}, {u0, v1}, q.color, 1, q.textureID };
|
||||||
|
// vertex_p2_st2_col4_a1_u32 vBR = { {x1, y1}, {u1, v1}, q.color, 1, q.textureID };
|
||||||
|
|
||||||
|
vertices.push_back(vTL);
|
||||||
|
vertices.push_back(vBL);
|
||||||
|
vertices.push_back(vTR);
|
||||||
|
|
||||||
|
vertices.push_back(vTR);
|
||||||
|
vertices.push_back(vBL);
|
||||||
|
vertices.push_back(vBR);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VkCommandBuffer cmd = frame.command_buffer;
|
||||||
|
vkResetCommandBuffer(cmd, 0);
|
||||||
|
|
||||||
|
VkCommandBufferBeginInfo cbBI {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
||||||
|
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT
|
||||||
|
};
|
||||||
|
vkBeginCommandBuffer(cmd, &cbBI);
|
||||||
|
|
||||||
|
recordCommandBuffer(
|
||||||
|
cmd,
|
||||||
|
images[imageIndex],
|
||||||
|
imageViews[imageIndex],
|
||||||
|
swapchain_extent,
|
||||||
|
imageLayouts[imageIndex],
|
||||||
|
frame,
|
||||||
|
vertices
|
||||||
|
);
|
||||||
|
|
||||||
|
vkEndCommandBuffer(cmd);
|
||||||
|
|
||||||
|
imageLayouts[imageIndex] = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||||
|
|
||||||
|
VkSemaphoreSubmitInfo waitBinary{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO,
|
||||||
|
.semaphore = frame.imageAvailable,
|
||||||
|
.stageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT
|
||||||
|
};
|
||||||
|
|
||||||
|
VkSemaphoreSubmitInfo signalBinary{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO,
|
||||||
|
.semaphore = renderFinished[imageIndex],
|
||||||
|
.stageMask = VK_PIPELINE_STAGE_2_ALL_GRAPHICS_BIT
|
||||||
|
};
|
||||||
|
|
||||||
|
VkCommandBufferSubmitInfo cmdInfo{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO,
|
||||||
|
.commandBuffer = cmd,
|
||||||
|
};
|
||||||
|
|
||||||
|
const VkSubmitInfo2 submit{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2,
|
||||||
|
.waitSemaphoreInfoCount = 1,
|
||||||
|
.pWaitSemaphoreInfos = &waitBinary,
|
||||||
|
.commandBufferInfoCount = 1,
|
||||||
|
.pCommandBufferInfos = &cmdInfo,
|
||||||
|
.signalSemaphoreInfoCount = 1,
|
||||||
|
.pSignalSemaphoreInfos = &signalBinary,
|
||||||
|
};
|
||||||
|
|
||||||
|
vkQueueSubmit2(graphics_queue, 1, &submit, frame.in_flight_fence);
|
||||||
|
|
||||||
|
VkPresentInfoKHR present{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
|
||||||
|
.waitSemaphoreCount = 1,
|
||||||
|
.pWaitSemaphores = &renderFinished[imageIndex],
|
||||||
|
.swapchainCount = 1,
|
||||||
|
.pSwapchains = &swapchain,
|
||||||
|
.pImageIndices = &imageIndex,
|
||||||
|
};
|
||||||
|
|
||||||
|
vkQueuePresentKHR(graphics_queue, &present);
|
||||||
|
|
||||||
|
currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::upload_vertex_buffer(
|
||||||
|
VkCommandBuffer cmd,
|
||||||
|
const Frame &frame,
|
||||||
|
std::span<const vertex_p2_s2_st2_col4_a1_u32> vertices) const
|
||||||
|
{
|
||||||
|
VkMemoryPropertyFlags memPropFlags;
|
||||||
|
vmaGetAllocationMemoryProperties(allocator, frame.vertexBuffer.allocation, &memPropFlags);
|
||||||
|
|
||||||
|
if(memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {
|
||||||
|
// The Allocation ended up in a mappable memory.
|
||||||
|
// Calling vmaCopyMemoryToAllocation() does vmaMapMemory(), memcpy(), vmaUnmapMemory(), and vmaFlushAllocation().
|
||||||
|
VkResult result = vmaCopyMemoryToAllocation(allocator, vertices.data(), frame.vertexBuffer.allocation, 0, vertices.size() * sizeof(vertex_p2_s2_st2_col4_a1_u32));
|
||||||
|
// Check result...
|
||||||
|
|
||||||
|
VkBufferMemoryBarrier bufMemBarrier = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER };
|
||||||
|
bufMemBarrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
|
||||||
|
bufMemBarrier.dstAccessMask = VK_ACCESS_UNIFORM_READ_BIT;
|
||||||
|
bufMemBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||||
|
bufMemBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||||
|
bufMemBarrier.buffer = frame.vertexBuffer.buffer;
|
||||||
|
bufMemBarrier.offset = 0;
|
||||||
|
bufMemBarrier.size = VK_WHOLE_SIZE;
|
||||||
|
|
||||||
|
// It's important to insert a buffer memory barrier here to ensure writing to the buffer has finished.
|
||||||
|
vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT,
|
||||||
|
0, 0, nullptr, 1, &bufMemBarrier, 0, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::recordCommandBuffer(
|
||||||
|
VkCommandBuffer cmd,
|
||||||
|
VkImage image,
|
||||||
|
VkImageView imageView,
|
||||||
|
VkExtent2D extent,
|
||||||
|
VkImageLayout oldLayout,
|
||||||
|
const Frame &frame,
|
||||||
|
const std::vector<vertex_p2_s2_st2_col4_a1_u32> &vertices) const
|
||||||
|
{
|
||||||
|
|
||||||
|
{
|
||||||
|
VkImageMemoryBarrier2 toColor{ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2 };
|
||||||
|
toColor.srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||||
|
toColor.dstStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||||
|
toColor.dstAccessMask = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT;
|
||||||
|
toColor.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||||
|
toColor.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||||
|
toColor.image = image;
|
||||||
|
toColor.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
|
||||||
|
|
||||||
|
VkDependencyInfo dep{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
|
||||||
|
.imageMemoryBarrierCount = 1,
|
||||||
|
.pImageMemoryBarriers = &toColor
|
||||||
|
};
|
||||||
|
vkCmdPipelineBarrier2(cmd, &dep);
|
||||||
|
}
|
||||||
|
|
||||||
|
VkClearValue clearColor = {{{0.1f, 0.1f, 0.2f, 1.0f}}};
|
||||||
|
VkRenderingAttachmentInfo colorAttach{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
|
||||||
|
.imageView = imageView,
|
||||||
|
.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||||||
|
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
|
||||||
|
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
|
||||||
|
.clearValue = clearColor
|
||||||
|
};
|
||||||
|
|
||||||
|
VkRenderingInfo ri{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_RENDERING_INFO,
|
||||||
|
.renderArea = {{0,0}, extent},
|
||||||
|
.layerCount = 1,
|
||||||
|
.colorAttachmentCount = 1,
|
||||||
|
.pColorAttachments = &colorAttach
|
||||||
|
};
|
||||||
|
|
||||||
|
upload_vertex_buffer(cmd, frame, vertices);
|
||||||
|
|
||||||
|
vkCmdBeginRendering(cmd, &ri);
|
||||||
|
|
||||||
|
VkViewport vp{0.0f, 0.0f, (float)extent.width, (float)extent.height, 0.0f, 1.0f};
|
||||||
|
VkRect2D sc{{0, 0}, extent};
|
||||||
|
vkCmdSetViewport(cmd, 0, 1, &vp);
|
||||||
|
vkCmdSetScissor(cmd, 0, 1, &sc);
|
||||||
|
|
||||||
|
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &set, 0, nullptr);
|
||||||
|
VkDeviceSize vOffset{ 0 };
|
||||||
|
vkCmdBindVertexBuffers(cmd, 0, 1, &frame.vertexBuffer.buffer, &vOffset);
|
||||||
|
|
||||||
|
glm::mat4 projection = glm::ortho(0.0f, (float)window_width, 0.0f, (float)window_height, -1.0f, 1.0f);
|
||||||
|
|
||||||
|
vkCmdPushConstants(
|
||||||
|
cmd,
|
||||||
|
pipelineLayout,
|
||||||
|
VK_SHADER_STAGE_VERTEX_BIT,
|
||||||
|
0,
|
||||||
|
sizeof(glm::mat4),
|
||||||
|
&projection
|
||||||
|
);
|
||||||
|
|
||||||
|
PipelineType lastPipeline = PipelineType::None; // Track current state
|
||||||
|
// uint32_t vertexOffset = currentFrame * MAX_VERTICES_PER_BATCH;
|
||||||
|
uint32_t currentBatchVertices = 0;
|
||||||
|
|
||||||
|
for (const auto & render_command : commands) {
|
||||||
|
// Only switch pipelines if we have to
|
||||||
|
if (render_command.pipeline != lastPipeline) {
|
||||||
|
// If we were mid-batch, draw what we have before switching
|
||||||
|
if (currentBatchVertices > 0) {
|
||||||
|
vkCmdDraw(cmd, currentBatchVertices, 1, 0, 0);
|
||||||
|
// vertexOffset += currentBatchVertices;
|
||||||
|
currentBatchVertices = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, get_pipeline(render_command.pipeline));
|
||||||
|
lastPipeline = render_command.pipeline;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentBatchVertices += 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw the final batch
|
||||||
|
if (currentBatchVertices > 0) {
|
||||||
|
vkCmdDraw(cmd, currentBatchVertices, 1, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vkCmdEndRendering(cmd);
|
||||||
|
|
||||||
|
// 3. Transition back to Present
|
||||||
|
{
|
||||||
|
VkImageMemoryBarrier2 toPresent{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
|
||||||
|
.srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||||
|
.srcAccessMask = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT,
|
||||||
|
.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||||
|
.dstAccessMask = 0,
|
||||||
|
.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||||||
|
.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
|
||||||
|
.image = image,
|
||||||
|
.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 },
|
||||||
|
};
|
||||||
|
|
||||||
|
VkDependencyInfo dep{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
|
||||||
|
.imageMemoryBarrierCount = 1,
|
||||||
|
.pImageMemoryBarriers = &toPresent
|
||||||
|
};
|
||||||
|
|
||||||
|
vkCmdPipelineBarrier2(cmd, &dep);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -15,7 +15,7 @@
|
|||||||
#include <glm/ext/matrix_clip_space.hpp>
|
#include <glm/ext/matrix_clip_space.hpp>
|
||||||
#include "glm/gtx/string_cast.hpp"
|
#include "glm/gtx/string_cast.hpp"
|
||||||
#include <vma/vk_mem_alloc.h>
|
#include <vma/vk_mem_alloc.h>
|
||||||
#include "sprite.h"
|
#include "../sprite.h"
|
||||||
#include "texture.h"
|
#include "texture.h"
|
||||||
#include <misc.h>
|
#include <misc.h>
|
||||||
#include <array>
|
#include <array>
|
||||||
@ -2,10 +2,68 @@
|
|||||||
// Created by Vicente Ferrari Smith on 12.02.26.
|
// Created by Vicente Ferrari Smith on 12.02.26.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#define VOLK_IMPLEMENTATION
|
||||||
|
#include <Volk/volk.h>
|
||||||
|
#define VMA_IMPLEMENTATION
|
||||||
|
#include <vma/vk_mem_alloc.h>
|
||||||
|
|
||||||
#include "init.h"
|
#include "init.h"
|
||||||
#include <print>
|
#include <print>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "../graphics.h"
|
||||||
|
|
||||||
|
VkInstance instance{};
|
||||||
|
VkPhysicalDevice physicalDevice{};
|
||||||
|
VkDevice device{};
|
||||||
|
VkQueue graphics_queue{};
|
||||||
|
uint32_t queueFamily{};
|
||||||
|
|
||||||
|
VkSurfaceKHR surface{};
|
||||||
|
VkDebugUtilsMessengerEXT debugMessenger{};
|
||||||
|
|
||||||
|
VmaAllocator allocator{};
|
||||||
|
|
||||||
|
constexpr uint32_t MAX_FRAMES_IN_FLIGHT = 2;
|
||||||
|
constexpr uint32_t MAX_VERTICES_PER_BATCH = 65536;
|
||||||
|
|
||||||
|
VkSwapchainKHR swapchain;
|
||||||
|
VkExtent2D swapchain_extent;
|
||||||
|
VkSurfaceFormatKHR swapchain_format{
|
||||||
|
VK_FORMAT_B8G8R8A8_UNORM,
|
||||||
|
VK_COLOR_SPACE_SRGB_NONLINEAR_KHR
|
||||||
|
};
|
||||||
|
std::vector<VkSemaphore> renderFinished;
|
||||||
|
|
||||||
|
std::vector<VkImage> images;
|
||||||
|
std::vector<VkImageView> imageViews;
|
||||||
|
std::vector<VkImageLayout> imageLayouts;
|
||||||
|
|
||||||
|
void graphics_init() {
|
||||||
|
createInstance(window);
|
||||||
|
createSurface(window);
|
||||||
|
createDevice();
|
||||||
|
|
||||||
|
createSwapchain(window);
|
||||||
|
|
||||||
|
slang::createGlobalSession(slangGlobalSession.writeRef());
|
||||||
|
|
||||||
|
Renderer renderer(window);
|
||||||
|
texture_manager.load("assets/boy.png", renderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void graphics_deinit() {
|
||||||
|
vkDeviceWaitIdle(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
void begin_frame() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void end_frame() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(
|
VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(
|
||||||
VkDebugUtilsMessageSeverityFlagBitsEXT severity,
|
VkDebugUtilsMessageSeverityFlagBitsEXT severity,
|
||||||
VkDebugUtilsMessageTypeFlagsEXT type,
|
VkDebugUtilsMessageTypeFlagsEXT type,
|
||||||
25
renderer/vulkan/vulkan.h
Normal file
25
renderer/vulkan/vulkan.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
//
|
||||||
|
// Created by Vicente Ferrari Smith on 12.02.26.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef V_INIT_H
|
||||||
|
#define V_INIT_H
|
||||||
|
|
||||||
|
#include <volk/volk.h>
|
||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
#include <vma/vk_mem_alloc.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
struct Device {
|
||||||
|
VkDevice device;
|
||||||
|
};
|
||||||
|
|
||||||
|
void createSwapchain(GLFWwindow* window);
|
||||||
|
|
||||||
|
int createInstance(GLFWwindow* window);
|
||||||
|
void createSurface(GLFWwindow* window);
|
||||||
|
void pickPhysicalDevice();
|
||||||
|
void createDevice();
|
||||||
|
|
||||||
|
|
||||||
|
#endif //V_INIT_H
|
||||||
5
renderer/webgpu/renderer.cpp
Normal file
5
renderer/webgpu/renderer.cpp
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
//
|
||||||
|
// Created by Vicente Ferrari Smith on 06.03.26.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "renderer.h"
|
||||||
16
renderer/webgpu/renderer.h
Normal file
16
renderer/webgpu/renderer.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
//
|
||||||
|
// Created by Vicente Ferrari Smith on 06.03.26.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef V_RENDERER_H
|
||||||
|
#define V_RENDERER_H
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class renderer {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif //V_RENDERER_H
|
||||||
79
renderer/webgpu/utils_emscripten.cpp
Normal file
79
renderer/webgpu/utils_emscripten.cpp
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
//
|
||||||
|
// Created by Vicente Ferrari Smith on 06.03.26.
|
||||||
|
//
|
||||||
|
|
||||||
|
// Copyright 2025 The Dawn & Tint Authors
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are met:
|
||||||
|
//
|
||||||
|
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
// list of conditions and the following disclaimer.
|
||||||
|
//
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
// this list of conditions and the following disclaimer in the documentation
|
||||||
|
// and/or other materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// 3. Neither the name of the copyright holder nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#if !defined(EMSCRIPTEN)
|
||||||
|
#error "utils_emscripten.cpp: This file requires EMSCRIPTEN to be defined."
|
||||||
|
#endif // !defined(EMSCRIPTEN)
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "GLFW/glfw3.h"
|
||||||
|
#include "emscripten/emscripten.h"
|
||||||
|
#include "webgpu/webgpu_glfw.h"
|
||||||
|
|
||||||
|
WGPU_GLFW_EXPORT WGPUSurface wgpuGlfwCreateSurfaceForWindow(const WGPUInstance instance,
|
||||||
|
GLFWwindow* window) {
|
||||||
|
wgpu::Surface s = wgpu::glfw::CreateSurfaceForWindow(instance, window);
|
||||||
|
return s.MoveToCHandle();
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace wgpu::glfw {
|
||||||
|
|
||||||
|
wgpu::Surface CreateSurfaceForWindow(const wgpu::Instance& instance, GLFWwindow* window) {
|
||||||
|
auto chainedDescriptor = SetupWindowAndGetSurfaceDescriptor(window);
|
||||||
|
|
||||||
|
wgpu::SurfaceDescriptor descriptor;
|
||||||
|
descriptor.nextInChain = chainedDescriptor.get();
|
||||||
|
wgpu::Surface surface = instance.CreateSurface(&descriptor);
|
||||||
|
|
||||||
|
return surface;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<wgpu::ChainedStruct, void (*)(wgpu::ChainedStruct*)>
|
||||||
|
SetupWindowAndGetSurfaceDescriptor(GLFWwindow* window) {
|
||||||
|
if (glfwGetWindowAttrib(window, GLFW_CLIENT_API) != GLFW_NO_API) {
|
||||||
|
emscripten_log(EM_LOG_ERROR,
|
||||||
|
"GL context was created on the window. Disable context creation by "
|
||||||
|
"setting the GLFW_CLIENT_API hint to GLFW_NO_API.");
|
||||||
|
return {nullptr, [](wgpu::ChainedStruct*) {}};
|
||||||
|
}
|
||||||
|
|
||||||
|
wgpu::EmscriptenSurfaceSourceCanvasHTMLSelector* desc =
|
||||||
|
new wgpu::EmscriptenSurfaceSourceCanvasHTMLSelector();
|
||||||
|
// Map "!canvas" CSS selector to the canvas held in the Module.canvas object.
|
||||||
|
EM_ASM({self.specialHTMLTargets && (specialHTMLTargets["!canvas"] = Module.canvas)});
|
||||||
|
desc->selector = "!canvas";
|
||||||
|
return {desc, [](wgpu::ChainedStruct* desc) {
|
||||||
|
delete reinterpret_cast<wgpu::EmscriptenSurfaceSourceCanvasHTMLSelector*>(desc);
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace wgpu::glfw
|
||||||
79
renderer/webgpu/utils_emscripten.h
Normal file
79
renderer/webgpu/utils_emscripten.h
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
//
|
||||||
|
// Created by Vicente Ferrari Smith on 06.03.26.
|
||||||
|
//
|
||||||
|
|
||||||
|
// Copyright 2025 The Dawn & Tint Authors
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are met:
|
||||||
|
//
|
||||||
|
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
// list of conditions and the following disclaimer.
|
||||||
|
//
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
// this list of conditions and the following disclaimer in the documentation
|
||||||
|
// and/or other materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// 3. Neither the name of the copyright holder nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#if !defined(EMSCRIPTEN)
|
||||||
|
#error "utils_emscripten.cpp: This file requires EMSCRIPTEN to be defined."
|
||||||
|
#endif // !defined(EMSCRIPTEN)
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "GLFW/glfw3.h"
|
||||||
|
#include "emscripten/emscripten.h"
|
||||||
|
#include "webgpu/webgpu_glfw.h"
|
||||||
|
|
||||||
|
WGPU_GLFW_EXPORT WGPUSurface wgpuGlfwCreateSurfaceForWindow(const WGPUInstance instance,
|
||||||
|
GLFWwindow* window) {
|
||||||
|
wgpu::Surface s = wgpu::glfw::CreateSurfaceForWindow(instance, window);
|
||||||
|
return s.MoveToCHandle();
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace wgpu::glfw {
|
||||||
|
|
||||||
|
wgpu::Surface CreateSurfaceForWindow(const wgpu::Instance& instance, GLFWwindow* window) {
|
||||||
|
auto chainedDescriptor = SetupWindowAndGetSurfaceDescriptor(window);
|
||||||
|
|
||||||
|
wgpu::SurfaceDescriptor descriptor;
|
||||||
|
descriptor.nextInChain = chainedDescriptor.get();
|
||||||
|
wgpu::Surface surface = instance.CreateSurface(&descriptor);
|
||||||
|
|
||||||
|
return surface;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<wgpu::ChainedStruct, void (*)(wgpu::ChainedStruct*)>
|
||||||
|
SetupWindowAndGetSurfaceDescriptor(GLFWwindow* window) {
|
||||||
|
if (glfwGetWindowAttrib(window, GLFW_CLIENT_API) != GLFW_NO_API) {
|
||||||
|
emscripten_log(EM_LOG_ERROR,
|
||||||
|
"GL context was created on the window. Disable context creation by "
|
||||||
|
"setting the GLFW_CLIENT_API hint to GLFW_NO_API.");
|
||||||
|
return {nullptr, [](wgpu::ChainedStruct*) {}};
|
||||||
|
}
|
||||||
|
|
||||||
|
wgpu::EmscriptenSurfaceSourceCanvasHTMLSelector* desc =
|
||||||
|
new wgpu::EmscriptenSurfaceSourceCanvasHTMLSelector();
|
||||||
|
// Map "!canvas" CSS selector to the canvas held in the Module.canvas object.
|
||||||
|
EM_ASM({self.specialHTMLTargets && (specialHTMLTargets["!canvas"] = Module.canvas)});
|
||||||
|
desc->selector = "!canvas";
|
||||||
|
return {desc, [](wgpu::ChainedStruct* desc) {
|
||||||
|
delete reinterpret_cast<wgpu::EmscriptenSurfaceSourceCanvasHTMLSelector*>(desc);
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace wgpu::glfw
|
||||||
5
renderer/webgpu/webgpu.cpp
Normal file
5
renderer/webgpu/webgpu.cpp
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
//
|
||||||
|
// Created by Vicente Ferrari Smith on 06.03.26.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "webgpu.h"
|
||||||
16
renderer/webgpu/webgpu.h
Normal file
16
renderer/webgpu/webgpu.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
//
|
||||||
|
// Created by Vicente Ferrari Smith on 06.03.26.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef V_WEBGPU_H
|
||||||
|
#define V_WEBGPU_H
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class webgpu {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif //V_WEBGPU_H
|
||||||
0
shaders/compute.slang
Normal file
0
shaders/compute.slang
Normal file
13
shaders/compute.wgsl
Normal file
13
shaders/compute.wgsl
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
@binding(1) @group(0) var<storage, read_write> output_buffer_0 : array<f32>;
|
||||||
|
|
||||||
|
@binding(0) @group(0) var<storage, read> input_buffer_0 : array<f32>;
|
||||||
|
|
||||||
|
@compute
|
||||||
|
@workgroup_size(32, 1, 1)
|
||||||
|
fn main(@builtin(global_invocation_id) thread_id_0 : vec3<u32>)
|
||||||
|
{
|
||||||
|
var _S1 : u32 = thread_id_0.x;
|
||||||
|
output_buffer_0[_S1] = 2.0f * input_buffer_0[_S1];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
BIN
shaders/shader.air
Normal file
BIN
shaders/shader.air
Normal file
Binary file not shown.
0
shaders/shader.metal
Normal file
0
shaders/shader.metal
Normal file
63
shaders/shader.wgsl
Normal file
63
shaders/shader.wgsl
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
struct _MatrixStorage_float4x4_ColMajorstd140_0
|
||||||
|
{
|
||||||
|
@align(16) data_0 : array<vec4<f32>, i32(4)>,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Uniforms_std140_0
|
||||||
|
{
|
||||||
|
@align(16) proj_0 : _MatrixStorage_float4x4_ColMajorstd140_0,
|
||||||
|
};
|
||||||
|
|
||||||
|
@binding(1) @group(0) var<uniform> uniforms_0 : Uniforms_std140_0;
|
||||||
|
@binding(0) @group(0) var colorTexture_0 : texture_2d<f32>;
|
||||||
|
|
||||||
|
@binding(2) @group(0) var samplerState_0 : sampler;
|
||||||
|
|
||||||
|
const square_0 : array<vec2<f32>, i32(6)> = array<vec2<f32>, i32(6)>( vec2<f32>(-0.5f, -0.5f), vec2<f32>(-0.5f, 0.5f), vec2<f32>(0.5f, -0.5f), vec2<f32>(0.5f, -0.5f), vec2<f32>(-0.5f, 0.5f), vec2<f32>(0.5f, 0.5f) );
|
||||||
|
struct VSOutput_0
|
||||||
|
{
|
||||||
|
@builtin(position) pos_0 : vec4<f32>,
|
||||||
|
uv_0 : vec2<f32>,
|
||||||
|
color_0 : vec4<f32>,
|
||||||
|
alpha_0 : f32,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct vertexInput_0
|
||||||
|
{
|
||||||
|
@location(0) pos_1 : vec2<f32>,
|
||||||
|
@location(1) scale_0 : vec2<f32>,
|
||||||
|
@location(2) uv_1 : vec2<f32>,
|
||||||
|
@location(3) color_1 : vec4<f32>,
|
||||||
|
@location(4) alpha_1 : f32,
|
||||||
|
};
|
||||||
|
|
||||||
|
@vertex
|
||||||
|
fn vs_main( _S1 : vertexInput_0, @builtin(vertex_index) vertex_index_0 : u32) -> VSOutput_0
|
||||||
|
{
|
||||||
|
var output_0 : VSOutput_0;
|
||||||
|
output_0.pos_0 = (((vec4<f32>(square_0[vertex_index_0 % u32(6)] * _S1.scale_0 + _S1.pos_1, 0.0f, 1.0f)) * (mat4x4<f32>(uniforms_0.proj_0.data_0[i32(0)][i32(0)], uniforms_0.proj_0.data_0[i32(1)][i32(0)], uniforms_0.proj_0.data_0[i32(2)][i32(0)], uniforms_0.proj_0.data_0[i32(3)][i32(0)], uniforms_0.proj_0.data_0[i32(0)][i32(1)], uniforms_0.proj_0.data_0[i32(1)][i32(1)], uniforms_0.proj_0.data_0[i32(2)][i32(1)], uniforms_0.proj_0.data_0[i32(3)][i32(1)], uniforms_0.proj_0.data_0[i32(0)][i32(2)], uniforms_0.proj_0.data_0[i32(1)][i32(2)], uniforms_0.proj_0.data_0[i32(2)][i32(2)], uniforms_0.proj_0.data_0[i32(3)][i32(2)], uniforms_0.proj_0.data_0[i32(0)][i32(3)], uniforms_0.proj_0.data_0[i32(1)][i32(3)], uniforms_0.proj_0.data_0[i32(2)][i32(3)], uniforms_0.proj_0.data_0[i32(3)][i32(3)]))));
|
||||||
|
output_0.uv_0 = _S1.uv_1;
|
||||||
|
output_0.color_0 = _S1.color_1;
|
||||||
|
output_0.alpha_0 = _S1.alpha_1;
|
||||||
|
return output_0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct pixelOutput_0
|
||||||
|
{
|
||||||
|
@location(0) output_1 : vec4<f32>,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pixelInput_0
|
||||||
|
{
|
||||||
|
@location(0) uv_2 : vec2<f32>,
|
||||||
|
@location(1) color_2 : vec4<f32>,
|
||||||
|
@location(2) alpha_2 : f32,
|
||||||
|
};
|
||||||
|
|
||||||
|
@fragment
|
||||||
|
fn fs_main( _S2 : pixelInput_0, @builtin(position) pos_2 : vec4<f32>) -> pixelOutput_0
|
||||||
|
{
|
||||||
|
var _S3 : pixelOutput_0 = pixelOutput_0( (textureSample((colorTexture_0), (samplerState_0), (_S2.uv_2))) * vec4<f32>(_S2.color_2.xyz, _S2.alpha_2) );
|
||||||
|
return _S3;
|
||||||
|
}
|
||||||
|
|
||||||
BIN
shaders/shaders.metallib
Normal file
BIN
shaders/shaders.metallib
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user