From e43edc67bd18150d6c7a43ffb3f418e6ade05bf3 Mon Sep 17 00:00:00 2001 From: Vicente Ferrari Smith Date: Sat, 16 May 2026 17:20:07 +0200 Subject: [PATCH] . --- .DS_Store | Bin 18436 -> 20484 bytes .gitignore | 3 ++ CMakeLists.txt | 8 ++++ main.cpp | 14 +++--- renderer/graphics.h | 2 +- renderer/metal/metal.cpp | 12 ++++- renderer/metal/metal.h | 2 +- renderer/metal/renderer.cpp | 86 ++++++++++++++++-------------------- renderer/metal/renderer.h | 1 + renderer/texture.cpp | 8 +++- 10 files changed, 74 insertions(+), 62 deletions(-) create mode 100644 .gitignore diff --git a/.DS_Store b/.DS_Store index 707a91fe7c777b2438139875ce6c76e0fa970a16..4a562860c109acf0cc2496e6fa962e57fdf967c9 100644 GIT binary patch delta 732 zcmZWnUr1A76hCLX`{!=+j?wPLZSCsHXq&oO(-~1?PB8l8K#C%0xZSah*1c|H3Q9zw zZ;g`kB-ALP=a}~)5|Mg{>><(TUh1KDA9@J_`&5!uKlr|L&X4owK34>9uIW zCM}8~sx#)D=%M@j%r`M;h833HB&YQ3uZ}U&x`#q8uP-CGvQxP`g{oFpl~Jv%Rg>j< zJ|lG2irVbOoTjF$`I%CFmOgfW@ZTsgVzM-szg4J~l*#F-TuChiF6^)o3^rQpiCqpP z(g$H6ap}Ne`t-plT!Y(CgNLvTub>5MunAva2Mx$$7Y1+u<2ZszOyL#0iWw~8T~sL+ zIHCWLo|8aeK^E#zf-)#%68&OZoSm&rXUkfaJ!O^72P=Hn{QO{aI4nn_LuY6; z=*360E%`eeETOH8)=!Pu`VaqF;Cl7J;7x9+Qp65o5e-Jh>4oU0WOPKIi|ktHtH{%0 z{45F5o;^u@mJQtB9eqHk8}-NGU$|6JD3aoIJJ(~>H(j3$D?W!oVuQ`>FpS&THTGvc Zb#LMvM-#l2W2?))%xzj*ka;cE?L$lQV^QCM#R2PiC)0g#$N`c7+Ax fW*H9~7GRJ9Jpl=0kI5-MGbgV$)ZW}+dWQ)BIV)=# diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..97b7afe --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +# Specify filepatterns you want git to ignore. +.DS_Store +build \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index d581385..3566ec8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -122,6 +122,14 @@ elseif (APPLE) renderer/metal/renderer.h ) + add_custom_command(TARGET v POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + "${CMAKE_SOURCE_DIR}/shaders" "$/shaders" + COMMAND ${CMAKE_COMMAND} -E copy_directory + "${CMAKE_SOURCE_DIR}/assets" "$/assets" + COMMENT "Copying shaders and assets to build directory" + ) + #[[ #shaders set(SHADER_DIR ${CMAKE_SOURCE_DIR}/shaders) diff --git a/main.cpp b/main.cpp index fd3d6eb..90c9195 100644 --- a/main.cpp +++ b/main.cpp @@ -22,12 +22,10 @@ GLFWwindow *window; int32_t window_width = 640; int32_t window_height = 480; -uint64_t t = 0; -uint64_t accumulator = 0; - -uint64_t dt = glfwGetTimerFrequency() / 60; // 1/60 s - -uint64_t current_time = glfwGetTimerValue(); +uint64_t t = 0; +uint64_t accumulator = 0; +uint64_t dt = 0; +uint64_t current_time = 0; sprite_t sprite { .origin = {0.5, 0.5}, @@ -44,6 +42,9 @@ int init() { if (!glfwInit()) return -1; + dt = glfwGetTimerFrequency() / 60; + current_time = glfwGetTimerValue(); + std::println("Hello, Sailor!"); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); @@ -87,7 +88,6 @@ void main_loop(void *data) { begin_frame(); double f = (t / (double) glfwGetTimerFrequency()); - std::println("{}", f); for (int x = sprite.scale.x / 2; x < window_width; x += sprite.scale.x) { submit_sprite({x + 30 * cos(f), 200 + 30 * sin(f)}, sprite); diff --git a/renderer/graphics.h b/renderer/graphics.h index ce337a2..0bca3c3 100644 --- a/renderer/graphics.h +++ b/renderer/graphics.h @@ -36,7 +36,7 @@ void graphics_deinit(); void begin_frame(); void end_frame(GLFWwindow *window); -void submit_quad(); +void submit_quad(glm::vec2 pos, glm::vec2 scale); void submit_sprite(glm::vec2 pos, const sprite_t &sprite); void upload_texture( diff --git a/renderer/metal/metal.cpp b/renderer/metal/metal.cpp index 34decbc..bb3acdd 100644 --- a/renderer/metal/metal.cpp +++ b/renderer/metal/metal.cpp @@ -39,6 +39,8 @@ void upload_texture( const void *pixels, Texture *texture) { + texture->p_texture = new PlatformTexture{}; + MTL::TextureDescriptor *td = MTL::TextureDescriptor::alloc()->init(); td->setPixelFormat(MTL::PixelFormatRGBA8Unorm); td->setWidth(w); @@ -73,13 +75,15 @@ void create_metal_layer(GLFWwindow *window) { 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)); + int fb_w, fb_h; + glfwGetFramebufferSize(window, &fb_w, &fb_h); + metal_layer->setDrawableSize(CGSizeMake(fb_w, fb_h)); CGColorSpaceRef p3Space = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); metal_layer->setColorspace(p3Space); + CGColorSpaceRelease(p3Space); } void load_metal_shader(const std::string &shader_path, @@ -201,6 +205,10 @@ void end_frame(GLFWwindow *window) { pPool->release(); } +void submit_quad(glm::vec2 pos, glm::vec2 scale) { + renderer.submit_quad({pos.x, pos.y}, {scale.x, scale.y}); +} + void submit_sprite(glm::vec2 pos, const sprite_t &sprite) { renderer.submit_sprite({pos.x, pos.y}, sprite); } diff --git a/renderer/metal/metal.h b/renderer/metal/metal.h index f1d1634..8eb1cd6 100644 --- a/renderer/metal/metal.h +++ b/renderer/metal/metal.h @@ -7,7 +7,7 @@ #include #define GLFW_EXPOSE_NATIVE_COCOA -#import +#include #include #include diff --git a/renderer/metal/renderer.cpp b/renderer/metal/renderer.cpp index 5424707..bdca99f 100644 --- a/renderer/metal/renderer.cpp +++ b/renderer/metal/renderer.cpp @@ -65,7 +65,7 @@ void Renderer::submit_quad(simd::float2 pos, simd::float2 scale) { RenderCommand cmd {}; cmd.pipeline = PipelineType::ColoredQuad; cmd.key = { - (uint16_t) pos.y, + (uint16_t) pos[1], 0, (uint8_t) PipelineType::ColoredQuad }; @@ -83,7 +83,7 @@ void Renderer::submit_sprite(simd::float2 pos, const sprite_t &sprite) { RenderCommand cmd {}; cmd.pipeline = PipelineType::TexturedQuad; cmd.key = { - (uint16_t) pos.y, + (uint16_t) pos[1], 0, (uint8_t) PipelineType::TexturedQuad }; @@ -247,6 +247,14 @@ void Renderer::create_render_pipeline() { } renderPipelineDescriptor->release(); vd->release(); + vertex_shader->release(); + fragment_shader->release(); + + MTL::SamplerDescriptor *sampler_desc = MTL::SamplerDescriptor::alloc()->init(); + sampler_desc->setMinFilter(MTL::SamplerMinMagFilterLinear); + sampler_desc->setMagFilter(MTL::SamplerMinMagFilterLinear); + sampler_state = metal_device.device->newSamplerState(sampler_desc); + sampler_desc->release(); } float4x4 make_ortho(float left, float right, float bottom, float top, float near, float far) { @@ -267,46 +275,34 @@ float4x4 make_ortho(float left, float right, float bottom, float top, float near } void Renderer::encode_render_command(GLFWwindow *window, MTL::RenderCommandEncoder *render_command_encoder) { + struct DrawGroup { + MTL::Texture *texture; + NS::UInteger vertex_start; + NS::UInteger vertex_count; + }; std::vector vertices; + std::vector groups; for (auto& cmd : commands) { - switch (cmd.pipeline) { - case PipelineType::ColoredQuad: { - const auto &q = cmd.colored_quad; - - vertex_p2_s2_uv2_c4_a1 vTL = { q.pos, q.scale, {}, q.colour }; - vertex_p2_s2_uv2_c4_a1 vTR = { q.pos, q.scale, {}, q.colour }; - vertex_p2_s2_uv2_c4_a1 vBL = { q.pos, q.scale, {}, q.colour }; - vertex_p2_s2_uv2_c4_a1 vBR = { q.pos, q.scale, {}, q.colour }; - - 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; - } case PipelineType::TexturedQuad: { const auto &q = cmd.textured_quad; + if (!q.texture) break; - vertex_p2_s2_uv2_c4_a1 vTL = { q.pos, q.scale, q.uv0, q.colour, 1.0 }; - vertex_p2_s2_uv2_c4_a1 vTR = { q.pos, q.scale, {q.uv1.x, q.uv0.y}, q.colour, 1.0 }; - vertex_p2_s2_uv2_c4_a1 vBL = { q.pos, q.scale, {q.uv0.x, q.uv1.y}, q.colour, 1.0 }; - vertex_p2_s2_uv2_c4_a1 vBR = { q.pos, q.scale, q.uv1, q.colour, 1.0}; + if (groups.empty() || groups.back().texture != q.texture) + groups.push_back({q.texture, (NS::UInteger)vertices.size(), 0}); - vertices.push_back(vTL); - vertices.push_back(vBL); - vertices.push_back(vTR); - - vertices.push_back(vTR); - vertices.push_back(vBL); - vertices.push_back(vBR); + simd::float2 uv_tr = {q.uv1[0], q.uv0[1]}; + simd::float2 uv_bl = {q.uv0[0], q.uv1[1]}; + vertices.push_back({ q.pos, q.scale, q.uv0, q.colour, 1.0f }); + vertices.push_back({ q.pos, q.scale, uv_bl, q.colour, 1.0f }); + vertices.push_back({ q.pos, q.scale, uv_tr, q.colour, 1.0f }); + vertices.push_back({ q.pos, q.scale, uv_tr, q.colour, 1.0f }); + vertices.push_back({ q.pos, q.scale, uv_bl, q.colour, 1.0f }); + vertices.push_back({ q.pos, q.scale, q.uv1, q.colour, 1.0f }); + groups.back().vertex_count += 6; break; } default: @@ -314,33 +310,26 @@ void Renderer::encode_render_command(GLFWwindow *window, MTL::RenderCommandEncod } } + if (vertices.empty()) return; + const Frame &frame = frames[current_frame]; - memcpy(frame.vertex_buffer->contents(), vertices.data(), vertices.size() * sizeof(vertex_p2_s2_uv2_c4_a1)); - MTL::SamplerDescriptor* sampler_desc = MTL::SamplerDescriptor::alloc()->init(); - - sampler_desc->setMinFilter(MTL::SamplerMinMagFilterLinear); - sampler_desc->setMagFilter(MTL::SamplerMinMagFilterLinear); - MTL::SamplerState* sampler = metal_device.device->newSamplerState(sampler_desc); - int width, height; glfwGetFramebufferSize(window, &width, &height); - simd::float4x4 ortho = make_ortho(0.0, window_width, window_height, 0.0, 1.0, 0.0); + simd::float4x4 ortho = make_ortho(0.0f, (float)width, (float)height, 0.0f, 1.0f, 0.0f); auto tm = simd::transpose(ortho); memcpy(frame.uniform_buffer->contents(), &tm, sizeof(ortho)); + render_command_encoder->setRenderPipelineState(textured_quad_pipeline); render_command_encoder->setVertexBuffer(frame.vertex_buffer, 0, 0); render_command_encoder->setVertexBuffer(frame.uniform_buffer, 0, 1); - render_command_encoder->setFragmentSamplerState(sampler, 0); - MTL::PrimitiveType type_triangle = MTL::PrimitiveTypeTriangle; - NS::UInteger vertexStart = 0; - NS::UInteger vertexCount = vertices.size(); - const Texture &texture = texture_manager.textures["assets/boy.png"]; - render_command_encoder->setFragmentTexture(texture.p_texture->texture, 0); - render_command_encoder->drawPrimitives(type_triangle, vertexStart, vertexCount); + render_command_encoder->setFragmentSamplerState(sampler_state, 0); - sampler_desc->release(); + for (const auto &group : groups) { + render_command_encoder->setFragmentTexture(group.texture, 0); + render_command_encoder->drawPrimitives(MTL::PrimitiveTypeTriangle, group.vertex_start, group.vertex_count); + } } void Renderer::send_render_command(GLFWwindow *window) { @@ -371,7 +360,6 @@ void Renderer::send_render_command(GLFWwindow *window) { command_buffer->presentDrawable(metal_drawable); command_buffer->commit(); - command_buffer->waitUntilCompleted(); current_frame = (current_frame + 1) % kMaxFramesInFlight; diff --git a/renderer/metal/renderer.h b/renderer/metal/renderer.h index cfe011c..fd692fb 100644 --- a/renderer/metal/renderer.h +++ b/renderer/metal/renderer.h @@ -114,6 +114,7 @@ struct Renderer { uint32_t nextTextureSlot = 0; + MTL::SamplerState *sampler_state{}; MTL::CommandBuffer *command_buffer{}; struct Frame { diff --git a/renderer/texture.cpp b/renderer/texture.cpp index 10e30d0..e3e51d0 100644 --- a/renderer/texture.cpp +++ b/renderer/texture.cpp @@ -2,6 +2,7 @@ // Created by Vicente Ferrari Smith on 01.03.26. // +#include #include "texture.h" #include "graphics.h" @@ -10,11 +11,14 @@ TextureManager::TextureManager() { } Texture TextureManager::load(const std::string& path) { - // Dedup: Don't load the same file twice! - // if (path_to_id.contains(path)) return path_to_id[path]; + if (textures.contains(path)) return textures[path]; int w, h, ch; unsigned char* data = stbi_load(path.c_str(), &w, &h, &ch, STBI_rgb_alpha); + if (!data) { + std::println("Failed to load texture: {}", path); + return {}; + } // Tell the renderer to make the GPU version Texture res;