.
This commit is contained in:
parent
6883d103ff
commit
e43edc67bd
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Specify filepatterns you want git to ignore.
|
||||||
|
.DS_Store
|
||||||
|
build
|
||||||
@ -122,6 +122,14 @@ elseif (APPLE)
|
|||||||
renderer/metal/renderer.h
|
renderer/metal/renderer.h
|
||||||
)
|
)
|
||||||
|
|
||||||
|
add_custom_command(TARGET v POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||||
|
"${CMAKE_SOURCE_DIR}/shaders" "$<TARGET_FILE_DIR:v>/shaders"
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||||
|
"${CMAKE_SOURCE_DIR}/assets" "$<TARGET_FILE_DIR:v>/assets"
|
||||||
|
COMMENT "Copying shaders and assets to build directory"
|
||||||
|
)
|
||||||
|
|
||||||
#[[ #shaders
|
#[[ #shaders
|
||||||
|
|
||||||
set(SHADER_DIR ${CMAKE_SOURCE_DIR}/shaders)
|
set(SHADER_DIR ${CMAKE_SOURCE_DIR}/shaders)
|
||||||
|
|||||||
14
main.cpp
14
main.cpp
@ -22,12 +22,10 @@ GLFWwindow *window;
|
|||||||
int32_t window_width = 640;
|
int32_t window_width = 640;
|
||||||
int32_t window_height = 480;
|
int32_t window_height = 480;
|
||||||
|
|
||||||
uint64_t t = 0;
|
uint64_t t = 0;
|
||||||
uint64_t accumulator = 0;
|
uint64_t accumulator = 0;
|
||||||
|
uint64_t dt = 0;
|
||||||
uint64_t dt = glfwGetTimerFrequency() / 60; // 1/60 s
|
uint64_t current_time = 0;
|
||||||
|
|
||||||
uint64_t current_time = glfwGetTimerValue();
|
|
||||||
|
|
||||||
sprite_t sprite {
|
sprite_t sprite {
|
||||||
.origin = {0.5, 0.5},
|
.origin = {0.5, 0.5},
|
||||||
@ -44,6 +42,9 @@ int init() {
|
|||||||
if (!glfwInit())
|
if (!glfwInit())
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
dt = glfwGetTimerFrequency() / 60;
|
||||||
|
current_time = glfwGetTimerValue();
|
||||||
|
|
||||||
std::println("Hello, Sailor!");
|
std::println("Hello, Sailor!");
|
||||||
|
|
||||||
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
|
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
|
||||||
@ -87,7 +88,6 @@ void main_loop(void *data) {
|
|||||||
begin_frame();
|
begin_frame();
|
||||||
|
|
||||||
double f = (t / (double) glfwGetTimerFrequency());
|
double f = (t / (double) glfwGetTimerFrequency());
|
||||||
std::println("{}", f);
|
|
||||||
|
|
||||||
for (int x = sprite.scale.x / 2; x < window_width; x += sprite.scale.x) {
|
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);
|
submit_sprite({x + 30 * cos(f), 200 + 30 * sin(f)}, sprite);
|
||||||
|
|||||||
@ -36,7 +36,7 @@ void graphics_deinit();
|
|||||||
void begin_frame();
|
void begin_frame();
|
||||||
void end_frame(GLFWwindow *window);
|
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 submit_sprite(glm::vec2 pos, const sprite_t &sprite);
|
||||||
|
|
||||||
void upload_texture(
|
void upload_texture(
|
||||||
|
|||||||
@ -39,6 +39,8 @@ void upload_texture(
|
|||||||
const void *pixels,
|
const void *pixels,
|
||||||
Texture *texture)
|
Texture *texture)
|
||||||
{
|
{
|
||||||
|
texture->p_texture = new PlatformTexture{};
|
||||||
|
|
||||||
MTL::TextureDescriptor *td = MTL::TextureDescriptor::alloc()->init();
|
MTL::TextureDescriptor *td = MTL::TextureDescriptor::alloc()->init();
|
||||||
td->setPixelFormat(MTL::PixelFormatRGBA8Unorm);
|
td->setPixelFormat(MTL::PixelFormatRGBA8Unorm);
|
||||||
td->setWidth(w);
|
td->setWidth(w);
|
||||||
@ -73,13 +75,15 @@ void create_metal_layer(GLFWwindow *window) {
|
|||||||
SEL setLayerSel = sel_registerName("setLayer:");
|
SEL setLayerSel = sel_registerName("setLayer:");
|
||||||
((void (*)(id, SEL, id))objc_msgSend)(content_view, setLayerSel, (id)metal_layer);
|
((void (*)(id, SEL, id))objc_msgSend)(content_view, setLayerSel, (id)metal_layer);
|
||||||
|
|
||||||
metal_layer->retain();
|
|
||||||
metal_layer->setDevice(metal_device.device);
|
metal_layer->setDevice(metal_device.device);
|
||||||
metal_layer->setPixelFormat(MTL::PixelFormatRGBA16Float);
|
metal_layer->setPixelFormat(MTL::PixelFormatRGBA16Float);
|
||||||
metal_layer->setFramebufferOnly(true);
|
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);
|
CGColorSpaceRef p3Space = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
|
||||||
metal_layer->setColorspace(p3Space);
|
metal_layer->setColorspace(p3Space);
|
||||||
|
CGColorSpaceRelease(p3Space);
|
||||||
}
|
}
|
||||||
|
|
||||||
void load_metal_shader(const std::string &shader_path,
|
void load_metal_shader(const std::string &shader_path,
|
||||||
@ -201,6 +205,10 @@ void end_frame(GLFWwindow *window) {
|
|||||||
pPool->release();
|
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) {
|
void submit_sprite(glm::vec2 pos, const sprite_t &sprite) {
|
||||||
renderer.submit_sprite({pos.x, pos.y}, sprite);
|
renderer.submit_sprite({pos.x, pos.y}, sprite);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
#include <GLFW/glfw3.h>
|
#include <GLFW/glfw3.h>
|
||||||
#define GLFW_EXPOSE_NATIVE_COCOA
|
#define GLFW_EXPOSE_NATIVE_COCOA
|
||||||
#import <GLFW/glfw3native.h>
|
#include <GLFW/glfw3native.h>
|
||||||
|
|
||||||
#include <Metal/Metal.hpp>
|
#include <Metal/Metal.hpp>
|
||||||
#include <QuartzCore/CAMetalLayer.hpp>
|
#include <QuartzCore/CAMetalLayer.hpp>
|
||||||
|
|||||||
@ -65,7 +65,7 @@ void Renderer::submit_quad(simd::float2 pos, simd::float2 scale) {
|
|||||||
RenderCommand cmd {};
|
RenderCommand cmd {};
|
||||||
cmd.pipeline = PipelineType::ColoredQuad;
|
cmd.pipeline = PipelineType::ColoredQuad;
|
||||||
cmd.key = {
|
cmd.key = {
|
||||||
(uint16_t) pos.y,
|
(uint16_t) pos[1],
|
||||||
0,
|
0,
|
||||||
(uint8_t) PipelineType::ColoredQuad
|
(uint8_t) PipelineType::ColoredQuad
|
||||||
};
|
};
|
||||||
@ -83,7 +83,7 @@ void Renderer::submit_sprite(simd::float2 pos, const sprite_t &sprite) {
|
|||||||
RenderCommand cmd {};
|
RenderCommand cmd {};
|
||||||
cmd.pipeline = PipelineType::TexturedQuad;
|
cmd.pipeline = PipelineType::TexturedQuad;
|
||||||
cmd.key = {
|
cmd.key = {
|
||||||
(uint16_t) pos.y,
|
(uint16_t) pos[1],
|
||||||
0,
|
0,
|
||||||
(uint8_t) PipelineType::TexturedQuad
|
(uint8_t) PipelineType::TexturedQuad
|
||||||
};
|
};
|
||||||
@ -247,6 +247,14 @@ void Renderer::create_render_pipeline() {
|
|||||||
}
|
}
|
||||||
renderPipelineDescriptor->release();
|
renderPipelineDescriptor->release();
|
||||||
vd->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) {
|
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) {
|
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<vertex_p2_s2_uv2_c4_a1> vertices;
|
std::vector<vertex_p2_s2_uv2_c4_a1> vertices;
|
||||||
|
std::vector<DrawGroup> groups;
|
||||||
|
|
||||||
for (auto& cmd : commands) {
|
for (auto& cmd : commands) {
|
||||||
|
|
||||||
switch (cmd.pipeline) {
|
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: {
|
case PipelineType::TexturedQuad: {
|
||||||
const auto &q = cmd.textured_quad;
|
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 };
|
if (groups.empty() || groups.back().texture != q.texture)
|
||||||
vertex_p2_s2_uv2_c4_a1 vTR = { q.pos, q.scale, {q.uv1.x, q.uv0.y}, q.colour, 1.0 };
|
groups.push_back({q.texture, (NS::UInteger)vertices.size(), 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};
|
|
||||||
|
|
||||||
vertices.push_back(vTL);
|
simd::float2 uv_tr = {q.uv1[0], q.uv0[1]};
|
||||||
vertices.push_back(vBL);
|
simd::float2 uv_bl = {q.uv0[0], q.uv1[1]};
|
||||||
vertices.push_back(vTR);
|
|
||||||
|
|
||||||
vertices.push_back(vTR);
|
|
||||||
vertices.push_back(vBL);
|
|
||||||
vertices.push_back(vBR);
|
|
||||||
|
|
||||||
|
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;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@ -314,33 +310,26 @@ void Renderer::encode_render_command(GLFWwindow *window, MTL::RenderCommandEncod
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (vertices.empty()) return;
|
||||||
|
|
||||||
const Frame &frame = frames[current_frame];
|
const Frame &frame = frames[current_frame];
|
||||||
|
|
||||||
memcpy(frame.vertex_buffer->contents(), vertices.data(), vertices.size() * sizeof(vertex_p2_s2_uv2_c4_a1));
|
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;
|
int width, height;
|
||||||
glfwGetFramebufferSize(window, &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);
|
auto tm = simd::transpose(ortho);
|
||||||
memcpy(frame.uniform_buffer->contents(), &tm, sizeof(ortho));
|
memcpy(frame.uniform_buffer->contents(), &tm, sizeof(ortho));
|
||||||
|
|
||||||
render_command_encoder->setRenderPipelineState(textured_quad_pipeline);
|
render_command_encoder->setRenderPipelineState(textured_quad_pipeline);
|
||||||
render_command_encoder->setVertexBuffer(frame.vertex_buffer, 0, 0);
|
render_command_encoder->setVertexBuffer(frame.vertex_buffer, 0, 0);
|
||||||
render_command_encoder->setVertexBuffer(frame.uniform_buffer, 0, 1);
|
render_command_encoder->setVertexBuffer(frame.uniform_buffer, 0, 1);
|
||||||
render_command_encoder->setFragmentSamplerState(sampler, 0);
|
render_command_encoder->setFragmentSamplerState(sampler_state, 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);
|
|
||||||
|
|
||||||
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) {
|
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->presentDrawable(metal_drawable);
|
||||||
command_buffer->commit();
|
command_buffer->commit();
|
||||||
command_buffer->waitUntilCompleted();
|
|
||||||
|
|
||||||
current_frame = (current_frame + 1) % kMaxFramesInFlight;
|
current_frame = (current_frame + 1) % kMaxFramesInFlight;
|
||||||
|
|
||||||
|
|||||||
@ -114,6 +114,7 @@ struct Renderer {
|
|||||||
|
|
||||||
uint32_t nextTextureSlot = 0;
|
uint32_t nextTextureSlot = 0;
|
||||||
|
|
||||||
|
MTL::SamplerState *sampler_state{};
|
||||||
MTL::CommandBuffer *command_buffer{};
|
MTL::CommandBuffer *command_buffer{};
|
||||||
|
|
||||||
struct Frame {
|
struct Frame {
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
// Created by Vicente Ferrari Smith on 01.03.26.
|
// Created by Vicente Ferrari Smith on 01.03.26.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#include <print>
|
||||||
#include "texture.h"
|
#include "texture.h"
|
||||||
#include "graphics.h"
|
#include "graphics.h"
|
||||||
|
|
||||||
@ -10,11 +11,14 @@ TextureManager::TextureManager() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Texture TextureManager::load(const std::string& path) {
|
Texture TextureManager::load(const std::string& path) {
|
||||||
// Dedup: Don't load the same file twice!
|
if (textures.contains(path)) return textures[path];
|
||||||
// if (path_to_id.contains(path)) return path_to_id[path];
|
|
||||||
|
|
||||||
int w, h, ch;
|
int w, h, ch;
|
||||||
unsigned char* data = stbi_load(path.c_str(), &w, &h, &ch, STBI_rgb_alpha);
|
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
|
// Tell the renderer to make the GPU version
|
||||||
Texture res;
|
Texture res;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user