diff --git a/CMakeLists.txt b/CMakeLists.txt index f1e24eb..5ed3bb5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,14 +2,25 @@ cmake_minimum_required(VERSION 3.3 FATAL_ERROR) project(padlab C) set(TARGET padlab) -option(USE_OPENGL "Use OpenGL for drawing (WIP)" OFF) -if (USE_OPENGL) +option(USE_METAL "use Metal for drawing" ${APPLE}) +if (USE_METAL) + option(USE_OPENGL "Use OpenGL for drawing (WIP)" OFF) option(USE_OPENGL_LEGACY "Use legacy OpenGL for drawing" OFF) - if (USE_OPENGL_LEGACY) - message(FATAL_ERROR "USE_OPENGL and USE_OPENGL_LEGACY are both ON but only one backend can be used at a time, turn one OFF and regenerate. (or delete cache and try again)") + if (USE_OPENGL) + message(FATAL_ERROR "USE_METAL and USE_OPENGL are both ON but only one backend can be used at a time, turn one OFF and regenerate. (or delete cache and try again)") + elseif (USE_OPENGL_LEGACY) + message(FATAL_ERROR "USE_METAL and USE_OPENGL_LEGACY are both ON but only one backend can be used at a time, turn one OFF and regenerate. (or delete cache and try again)") endif() else() - option(USE_OPENGL_LEGACY "Use legacy OpenGL for drawing" ON) + option(USE_OPENGL "Use OpenGL for drawing (WIP)" OFF) + if (USE_OPENGL) + option(USE_OPENGL_LEGACY "Use legacy OpenGL for drawing" OFF) + if (USE_OPENGL_LEGACY) + message(FATAL_ERROR "USE_OPENGL and USE_OPENGL_LEGACY are both ON but only one backend can be used at a time, turn one OFF and regenerate. (or delete cache and try again)") + endif() + else() + option(USE_OPENGL_LEGACY "Use legacy OpenGL for drawing" ON) + endif() endif() list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") diff --git a/cmake/BinHelper.cmake b/cmake/BinHelper.cmake index 8825c40..2bab4b7 100644 --- a/cmake/BinHelper.cmake +++ b/cmake/BinHelper.cmake @@ -10,14 +10,18 @@ function (bin2h_compile) set(DEPENDS) set(COMMAND ${BIN2H_EXECUTABLE}) - foreach (BIN ${ARGS_BIN}) - set(SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/${BIN}) + foreach (SOURCE ${ARGS_BIN}) + if (NOT IS_ABSOLUTE ${SOURCE}) + set(SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/${SOURCE}) + endif() list(APPEND DEPENDS ${SOURCE}) list(APPEND COMMAND "-b" "${SOURCE}") endforeach() - foreach (TXT ${ARGS_TXT}) - set(SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/${TXT}) + foreach (SOURCE ${ARGS_TXT}) + if (NOT IS_ABSOLUTE ${SOURCE}) + set(SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/${SOURCE}) + endif() list(APPEND DEPENDS ${SOURCE}) list(APPEND COMMAND "-t" "${SOURCE}") endforeach() diff --git a/cmake/MetalHelper.cmake b/cmake/MetalHelper.cmake new file mode 100644 index 0000000..c29c2d2 --- /dev/null +++ b/cmake/MetalHelper.cmake @@ -0,0 +1,55 @@ +include(CMakeParseArguments) # 3.4 and lower compatibility + +function (_xrun_find_program OUTPUT NAME) + find_program(XCRUN_EXECUTABLE xcrun REQUIRED) + execute_process(COMMAND ${XCRUN_EXECUTABLE} -sdk macosx -f ${NAME} OUTPUT_VARIABLE EXECUTABLE_PATH) + get_filename_component(EXECUTABLE_DIR ${EXECUTABLE_PATH} DIRECTORY) + find_program(${OUTPUT} ${NAME} PATHS ${EXECUTABLE_DIR} REQUIRED) +endfunction() + +function (metal_compile) + cmake_parse_arguments(ARGS "DEBUG" "OUTPUT" "SOURCES;CFLAGS" ${ARGN}) + + _xrun_find_program(METAL_EXECUTABLE metal) + + if (ARGS_DEBUG) + _xrun_find_program(METAL_DSYMUTIL_EXECUTABLE metal-dsymutil) + list(APPEND CFLAGS -frecord-sources) + else() + _xrun_find_program(METALLIB_EXECUTABLE metallib) + endif() + + set(AIR_OBJECTS) + foreach (SOURCE ${ARGS_SOURCES}) + if (${CMAKE_VERSION} VERSION_GREATER 3.3) + get_filename_component(SOURCE "${SOURCE}" REALPATH) + else() + if (NOT IS_ABSOLUTE ${SOURCE}) + set(SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/${SOURCE}) + endif() + endif() + get_filename_component(BASENAME "${SOURCE}" NAME) + set(OUTPUT_AIR ${BASENAME}.air) + add_custom_command( + COMMAND ${METAL_EXECUTABLE} + ARGS ${ARGS_CFLAGS} -c ${SOURCE} -o ${OUTPUT_AIR} + DEPENDS ${METAL_EXECUTABLE} ${SOURCE} + OUTPUT ${OUTPUT_AIR}) + list(APPEND AIR_OBJECTS ${OUTPUT_AIR}) + endforeach() + + if (NOT ARGS_DEBUG) + add_custom_command( + COMMAND ${METALLIB_EXECUTABLE} + ARGS ${LDFLAGS} ${AIR_OBJECTS} -o ${ARGS_OUTPUT} + DEPENDS ${METALLIB_EXECUTABLE} ${AIR_OBJECTS} + OUTPUT ${ARGS_OUTPUT}) + else() + set(OUTPUTSYM ${ARGS_OUTPUT}sym) + add_custom_command( + COMMAND ${METAL_EXECUTABLE} ARGS -frecord-sources ${AIR_OBJECTS} -o ${ARGS_OUTPUT} + COMMAND ${METAL_DSYMUTIL_EXECUTABLE} ARGS -flat -remove-source ${ARGS_OUTPUT} + DEPENDS ${METAL_EXECUTABLE} ${METAL_DSYMUTIL_EXECUTABLE} ${AIR_OBJECTS} + OUTPUT ${ARGS_OUTPUT} ${OUTPUTSYM}) + endif() +endfunction() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ea82275..707698c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,14 +2,24 @@ set(SOURCES maths.h draw.h draw_common.c - $<$,$>>:draw.c> + $<$,$,$>>:draw.c> + $<$:draw_metal.m metal_shader_types.h> $<$:draw_opengl_core.c> $<$:draw_opengl.c> stick.h stick.c analogue.c) -if (USE_OPENGL) +if (USE_METAL) + find_library(METAL Metal REQUIRED) + find_library(FOUNDATION Foundation REQUIRED) + find_library(QUARTZCORE QuartzCore REQUIRED) + include(MetalHelper) + metal_compile(OUTPUT shader.metallib SOURCES shader.metal) + include(BinHelper) + bin2h_compile(OUTPUT metalShader.h BIN ${CMAKE_CURRENT_BINARY_DIR}/shader.metallib) + list(APPEND SOURCES ${CMAKE_CURRENT_BINARY_DIR}/metalShader.h) +elseif (USE_OPENGL) include(GL3WHelper) add_gl3w(gl3w) include(BinHelper) @@ -19,14 +29,16 @@ endif() add_executable(${TARGET} ${SOURCES}) target_include_directories(${TARGET} PRIVATE - $<$:${CMAKE_CURRENT_BINARY_DIR}>) + $<$,$>:${CMAKE_CURRENT_BINARY_DIR}>) target_link_libraries(${TARGET} $<$:SDL2::SDL2main> SDL2::SDL2 + $<$:${METAL} ${QUARTZCORE} ${FOUNDATION}> $<$,$>:OpenGL::GL> $<$:gl3w> $<$:m>) target_compile_options(${TARGET} PRIVATE $<$:-Wall -Wextra -pedantic -Wno-unused-parameter>) target_compile_definitions(${TARGET} PRIVATE + $<$:USE_METAL> $<$,$>:USE_OPENGL>) diff --git a/src/analogue.c b/src/analogue.c index c3331cb..f20e645 100644 --- a/src/analogue.c +++ b/src/analogue.c @@ -37,6 +37,8 @@ int main(int argc, char** argv) const int winpos = SDL_WINDOWPOS_CENTERED; #ifdef USE_OPENGL const int winflg = SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL | SDL_WINDOW_ALLOW_HIGHDPI; +#elif defined USE_METAL + const int winflg = SDL_WINDOW_RESIZABLE | SDL_WINDOW_METAL | SDL_WINDOW_ALLOW_HIGHDPI; #else const int winflg = SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI; #endif diff --git a/src/draw_metal.m b/src/draw_metal.m new file mode 100644 index 0000000..fe5d9b2 --- /dev/null +++ b/src/draw_metal.m @@ -0,0 +1,430 @@ +#include "draw.h" +#include "metalShader.h" +#include "metal_shader_types.h" +#include "maths.h" +#include + +#import +#import + + +@interface MetalRenderer : NSObject + +@property (nonatomic, readwrite) uint32_t drawColour; + +- (id) init:(SDL_Window*)window; +- (void) dealloc; + +- (unsigned) resizeBuffer:(void**)buf itemSize:(unsigned)nsz reserveLen:(unsigned)reserve requiredNum:(unsigned)count; +- (void) resizeMtlBuffer:(id *)buf length:(unsigned)length; + +- (size) getDrawSize; +- (void) setClear:(uint32_t)colour; +- (void) setView:(size)viewportSize; +- (void) clearVerticesAndIndices; +- (void) reserveVertices:(unsigned)count; +- (void) reserveIndices:(unsigned)count; +- (uint16_t) queueVertex:(int)x :(int)y; +- (uint16_t) queueVertexF:(float)x :(float)y; +- (uint16_t) queueIndex:(uint16_t)idx; +- (void) queueIndices:(uint16_t*)idcs count:(unsigned)count; +- (void) present; + +@end + +#define DRAWLIST_CHUNK_SIZE 480 +#define DRAWLIST_INIT_SIZE (DRAWLIST_CHUNK_SIZE) + +@implementation MetalRenderer +{ + SDL_Window* _window; + SDL_MetalView _view; + id _dev; + CAMetalLayer* _layer; + id _queue; + MTLRenderPassDescriptor* _passDesc; + id _pso; // gonna fine u on the belgrave & lillydale line + MTLViewport _viewport; + vector_float4 _drawColourF; + + ShaderVertex* _vtxList; + uint16_t* _idxList; + unsigned _vtxListCount, _vtxListReserve, _idxListCount, _idxListReserve; + id _vtxMtlBuffer, _idxMtlBuffer; +} + +- (id) init:(SDL_Window*)window +{ + if (!(self = [super init])) + return nil; + + self.drawColour = BLACK; + _vtxList = NULL; + _idxList = NULL; + _vtxListCount = 0; + _idxListCount = 0; + _vtxListReserve = 0; + _idxListReserve = 0; + _vtxMtlBuffer = nil; + _idxMtlBuffer = nil; + + // Create Metal view + _window = window; + _view = SDL_Metal_CreateView(_window); + + // Get Metal device +#if 1 + _dev = MTLCreateSystemDefaultDevice(); + fprintf(stderr, "Default MTL device \"%s\"\n", [_dev.name UTF8String]); +#else + NSArray>* devices = MTLCopyAllDevices(); + for (id i in devices) + { + if (!i.supportsRaytracing) continue; + if (_dev && i.isLowPower) continue; + _dev = i; + } + fprintf(stderr, "You have selected funny device \"%s\"\n", [_dev.name UTF8String]); +#endif + + // Setup Metal layer + _layer = (__bridge CAMetalLayer*)SDL_Metal_GetLayer(_view); + _layer.device = _dev; + _layer.pixelFormat = MTLPixelFormatBGRA8Unorm; + + _queue = [_dev newCommandQueue]; + _passDesc = [MTLRenderPassDescriptor new]; + _passDesc.colorAttachments[0].loadAction = MTLLoadActionClear; + _passDesc.colorAttachments[0].storeAction = MTLStoreActionStore; + [self setClear:self.drawColour]; // passDesc.colorAttachments[0].clearColor = curColour + + // Compile shady bois + __autoreleasing NSError* booboo = nil; + dispatch_data_t shaderData = dispatch_data_create(shader_metallib, SHADER_METALLIB_SIZE, nil, nil); + id lib = [_dev newLibraryWithData:shaderData error:&booboo]; + if (!lib) + { + fprintf(stderr, "Metal shader compilation failed:\n%s\n", [[booboo localizedDescription] UTF8String]); + return nil; + } + id vertPrg = [lib newFunctionWithName:@"vertexMain"]; + id fragPrg = [lib newFunctionWithName:@"fragmentMain"]; + + // Setup render pipeline state + MTLRenderPipelineDescriptor* pipeDesc = [[MTLRenderPipelineDescriptor alloc] init]; + pipeDesc.vertexFunction = vertPrg; + pipeDesc.fragmentFunction = fragPrg; + pipeDesc.colorAttachments[0].pixelFormat = _layer.pixelFormat; + _pso = [_dev newRenderPipelineStateWithDescriptor:pipeDesc error:&booboo]; + if (!_pso) + { + fprintf(stderr, "Your Myki has expired and u have been slapped with a $237 fine: %s\n", [[booboo localizedDescription] UTF8String]); + return nil; + } + [pipeDesc release]; + + // Set viewport + [self setView:[self getDrawSize]]; + + return self; +} + +- (void) dealloc +{ + SDL_Metal_DestroyView(_view); + [super dealloc]; +} + + +- (unsigned) resizeBuffer:(void**)buf itemSize:(unsigned)nsz reserveLen:(unsigned)reserve requiredNum:(unsigned)count +{ + if (*buf && count * nsz <= reserve) + return reserve; + + // Calculate new capacity + unsigned newCapacity = (count + DRAWLIST_CHUNK_SIZE - 1) / DRAWLIST_CHUNK_SIZE * DRAWLIST_CHUNK_SIZE; + if (!*buf) + newCapacity = MAX(newCapacity, DRAWLIST_INIT_SIZE); + + // (Re)allocate and return new reserve size + unsigned newReserve = newCapacity * nsz; + *buf = realloc(*buf, newReserve); + printf("buffer %p %u -> %u\n", *buf, reserve, newReserve); + return newReserve; +} + +- (void) resizeMtlBuffer:(id *)buf length:(unsigned)length +{ + if (*buf && length <= [*buf length]) + return; + + printf("mtlBuf %p %lu -> %u\n", (void*)buf, (unsigned long)[*buf length], length); + id new = [_dev newBufferWithLength:length options:MTLResourceStorageModeManaged]; + if (*buf) + { + [*buf setPurgeableState:MTLPurgeableStateEmpty]; + [*buf release]; + } + *buf = new; +} + + +- (size) getDrawSize +{ + size out; + SDL_Metal_GetDrawableSize(_window, &out.w, &out.h); + return out; +} + +- (void)setDrawColour:(uint32_t)colour +{ + _drawColour = colour; + const float mul = 1.0f / 255.0f; + _drawColourF = (vector_float4){ + (float)((colour & 0xFF000000) >> 24) * mul, + (float)((colour & 0x00FF0000) >> 16) * mul, + (float)((colour & 0x0000FF00) >> 8) * mul, + (float)((colour & 0x000000FF)) * mul }; +} + +- (void) setClear:(uint32_t)colour +{ + const double mul = 1.0 / 255.0; + _passDesc.colorAttachments[0].clearColor = MTLClearColorMake( + (double)((colour & 0xFF000000) >> 24) * mul, + (double)((colour & 0x00FF0000) >> 16) * mul, + (double)((colour & 0x0000FF00) >> 8) * mul, + (double)((colour & 0x000000FF)) * mul); +} + +- (void) setView:(size)viewportSize +{ + _viewport = (MTLViewport){ + .originX = 0.0, .originY = 0.0, + .width = viewportSize.w, .height = viewportSize.h, + .znear = 1.0, .zfar = -1.0 }; +} + +- (void) clearVerticesAndIndices +{ + _vtxListCount = 0; + _idxListCount = 0; +} + +- (void) reserveVertices:(unsigned)count +{ + count += _vtxListCount; + if (count * sizeof(ShaderVertex) > _vtxListReserve) + _vtxListReserve = [self resizeBuffer:(void**)&_vtxList + itemSize:sizeof(ShaderVertex) reserveLen:_vtxListReserve requiredNum:count]; +} + +- (void) reserveIndices:(unsigned)count +{ + count += _idxListCount; + if (count * sizeof(uint16_t) > _idxListReserve) + _idxListReserve = [self resizeBuffer:(void**)&_idxList + itemSize:sizeof(uint16_t) reserveLen:_idxListReserve requiredNum:count]; +} + +- (uint16_t) queueVertex:(int)x :(int)y +{ + if (_vtxListCount * sizeof(ShaderVertex) >= _vtxListReserve) + [self reserveVertices:1]; + _vtxList[_vtxListCount] = (ShaderVertex){ + .position = { (float)x, (float)y }, + .colour = _drawColourF }; + return _vtxListCount++; +} + +- (uint16_t) queueVertexF:(float)x :(float)y +{ + if (_vtxListCount * sizeof(ShaderVertex) >= _vtxListReserve) + [self reserveVertices:1]; + _vtxList[_vtxListCount] = (ShaderVertex){ + .position = { x, y }, + .colour = _drawColourF }; + return _vtxListCount++; +} + +- (uint16_t) queueIndex:(uint16_t)idx +{ + if (_idxListCount * sizeof(uint16_t) >= _idxListReserve) + [self reserveIndices:1]; + _idxList[_idxListCount++] = idx; + return idx; +} + +- (void) queueIndices:(uint16_t*)idcs count:(unsigned)count +{ + if ((_idxListCount + count) * sizeof(uint16_t) >= _idxListReserve) + [self reserveIndices:count]; + memcpy(&_idxList[_idxListCount], idcs, count * sizeof(uint16_t)); + _idxListCount += count; +} + +- (void) present +{ + // Create or recreate buffers if needed & fill with data + if (_vtxListCount) + { + unsigned copyLen = _vtxListCount * sizeof(ShaderVertex); + if (_vtxMtlBuffer == nil || [_vtxMtlBuffer length] < copyLen) + [self resizeMtlBuffer:&_vtxMtlBuffer length:_vtxListReserve]; + memcpy(_vtxMtlBuffer.contents, _vtxList, copyLen); + } + if (_idxListCount) + { + unsigned copyLen = _idxListCount * sizeof(uint16_t); + if (_idxMtlBuffer == nil || [_idxMtlBuffer length] < copyLen) + [self resizeMtlBuffer:&_idxMtlBuffer length:_idxListReserve]; + memcpy(_idxMtlBuffer.contents, _idxList, copyLen); + } + + @autoreleasepool + { + id rt = [_layer nextDrawable]; + _passDesc.colorAttachments[0].texture = rt.texture; + + id cmdBuf = [_queue commandBuffer]; + id enc = [cmdBuf renderCommandEncoderWithDescriptor:_passDesc]; + + [enc setViewport:_viewport]; + [enc setCullMode:MTLCullModeNone]; + [enc setRenderPipelineState:_pso]; + + if (_vtxMtlBuffer != nil && _idxMtlBuffer != nil) + { + [enc setVertexBuffer:_vtxMtlBuffer offset:0 atIndex:ShaderInputIdxVerticies]; + const vector_float2 viewportScale = { (float)(1.0 / _viewport.width), (float)(1.0 / _viewport.height) }; + [enc setVertexBytes:&viewportScale length:sizeof(vector_float2) atIndex:ShaderInputViewportScale]; + [enc drawIndexedPrimitives:MTLPrimitiveTypeLine + indexCount:_idxListCount indexType:MTLIndexTypeUInt16 + indexBuffer:_idxMtlBuffer indexBufferOffset:0]; + } + + [enc endEncoding]; + [cmdBuf presentDrawable:rt]; // u go to software premieres + [cmdBuf commit]; // is that linus torvolds? + // hear songs on pandora radiowo + + [self clearVerticesAndIndices]; + } +} + +@end + + +static MetalRenderer* renderer = nil; + +void DrawWindowHints(void) {} + +int InitDraw(SDL_Window* window) +{ + renderer = [[MetalRenderer alloc] init:window]; + if (!renderer) + return -1; + + return 0; +} + + +void QuitDraw(void) +{ + [renderer release]; +} + + +size GetDrawSizeInPixels(void) +{ + return renderer ? [renderer getDrawSize] : (size){ 0, 0 }; +} + + +void SetDrawViewport(size size) +{ + [renderer setView:size]; +} + + +void SetDrawColour(uint32_t c) +{ + renderer.drawColour = c; +} + + +void DrawClear(void) +{ + [renderer setClear:renderer.drawColour]; + [renderer clearVerticesAndIndices]; +} + + +void DrawPoint(int x, int y) +{ + DrawCircleSteps(x, y, 1, 4); +} + + +void DrawRect(int x, int y, int w, int h) +{ + [renderer reserveVertices:4]; + uint16_t i00 = [renderer queueVertex:x :y]; + uint16_t i10 = [renderer queueVertex:x + w :y]; + uint16_t i11 = [renderer queueVertex:x + w :y + h]; + uint16_t i01 = [renderer queueVertex:x :y + h]; + uint16_t indices[] = { i00, i10, i10, i11, i11, i01, i01, i00 }; + [renderer queueIndices:indices count:sizeof(indices) / sizeof(uint16_t)]; +} + + +void DrawLine(int x1, int y1, int x2, int y2) +{ + [renderer queueIndex:[renderer queueVertex:x1 :y1]]; + [renderer queueIndex:[renderer queueVertex:x2 :y2]]; +} + + +void DrawCircleSteps(int x, int y, int r, int steps) +{ + const float fx = (float)x, fy = (float)y; + const float stepSz = (float)TAU / (float)abs(steps); + const float mag = (float)r; + + // Draw whole circle in a single loop + [renderer reserveVertices:steps]; + [renderer reserveIndices:steps * 2]; + uint16_t base = [renderer queueIndex:[renderer queueVertex:x + r :y]]; + for (int i = 1; i < steps; ++i) + { + const float theta = stepSz * (float)i; + uint16_t ii = [renderer queueVertexF:fx + cosf(theta) * mag :fy + sinf(theta) * mag]; + [renderer queueIndices:(uint16_t[]){ ii, ii } count:2]; + } + [renderer queueIndex:base]; +} + + +void DrawArcSteps(int x, int y, int r, int startAng, int endAng, int steps) +{ + const float fx = (float)x, fy = (float)y; + const float magw = (float)r, magh = (float)r; + + const float start = (float)startAng * (float)DEG2RAD; + const float stepSz = (float)(endAng - startAng) / (float)abs(steps) * (float)DEG2RAD; + [renderer reserveVertices:steps]; + [renderer reserveIndices:steps * 2]; + uint16_t ii = [renderer queueVertexF:fx + cosf(start) * magw :fy - sinf(start) * magh]; + for (int i = 1; i <= steps; ++i) + { + const float theta = start + stepSz * (float)i; + uint16_t iii = [renderer queueVertexF:fx + cosf(theta) * magw :fy - sinf(theta) * magh]; + [renderer queueIndices:(uint16_t[]){ ii, iii } count:2]; + ii = iii; + } +} + +void DrawPresent(void) +{ + [renderer present]; +} diff --git a/src/metal_shader_types.h b/src/metal_shader_types.h new file mode 100644 index 0000000..962fdfe --- /dev/null +++ b/src/metal_shader_types.h @@ -0,0 +1,18 @@ +#ifndef METAL_SHADER_TYPES_H +#define METAL_SHADER_TYPES_H + +#include + +typedef enum +{ + ShaderInputIdxVerticies = 0, + ShaderInputViewportScale = 1 +} ShaderInputIdx; + +typedef struct +{ + vector_float2 position; + vector_float4 colour; +} ShaderVertex; + +#endif//METAL_SHADER_TYPES_H diff --git a/src/shader.metal b/src/shader.metal new file mode 100644 index 0000000..231508e --- /dev/null +++ b/src/shader.metal @@ -0,0 +1,27 @@ +#include "metal_shader_types.h" +#include +using namespace metal; + +struct FragmentInput +{ + float4 position [[position]]; + float4 colour; +}; + +vertex FragmentInput vertexMain( + uint vertexId [[vertex_id]], + device const ShaderVertex* v [[buffer(ShaderInputIdxVerticies)]], + constant vector_float2& viewportScale [[buffer(ShaderInputViewportScale)]]) +{ + float2 eyePos = v[vertexId].position * viewportScale * float2(2.0f, -2.0f) - float2(1.0f, -1.0f); + + FragmentInput o; + o.position = float4(eyePos, 0.0f, 1.0f); + o.colour = v[vertexId].colour; + return o; +} + +fragment float4 fragmentMain(FragmentInput in [[stage_in]]) +{ + return in.colour; +} diff --git a/src/util.h b/src/util.h index 6adc3c1..4300e05 100644 --- a/src/util.h +++ b/src/util.h @@ -20,7 +20,8 @@ typedef struct { int x, y, w, h; } rect; #define MKRGB(C) (uint32_t)(((C) << 8) | 0x000000FF) -#define WHITE 0xFFFFFFFF +#define BLACK MKRGB(0x000000) +#define WHITE MKRGB(0xFFFFFF) #define GREY1 MKGREY(0x1F, 0xFF) #define GREY2 MKGREY(0x37, 0xFF) #define GREY3 MKGREY(0x4F, 0xFF) diff --git a/tools/bin2h.py b/tools/bin2h.py index d79546a..85399be 100644 --- a/tools/bin2h.py +++ b/tools/bin2h.py @@ -3,6 +3,7 @@ import sys from pathlib import Path from typing import BinaryIO, TextIO +from os import SEEK_SET, SEEK_END import re @@ -16,7 +17,24 @@ def sanitise_label(label: str) -> str: def bin2h(name: str, binf: BinaryIO, h: TextIO): - raise NotImplementedError + label = sanitise_label(name) + + binf.seek(0, SEEK_END) + length = binf.tell() + binf.seek(0, SEEK_SET) + h.write(f"#define {label.upper()}_SIZE {length}\n") + + whitespace = "\t" + h.write(f"static const unsigned char {label}[{length}] = {{\n{whitespace}") + + for i, c in enumerate(binf.read()): + if i != 0: + h.write(", ") + if i % 16 == 0: + h.write(f"\n{whitespace}") + h.write(f"0x{c:02X}") + + h.write("\n};\n") def txt2h(name: str, txt: TextIO, h: TextIO): @@ -93,16 +111,16 @@ def main(): path = Path(i) with path.open("rb") as file: bin2h(path.name, file, h) + h.write("\n") # Write texts for i in txtfiles: path = Path(i) with path.open("r") as file: txt2h(path.name, file, h) + h.write("\n") - h.writelines([ - "\n", - f"#endif//{guard}\n"]) + h.write(f"#endif//{guard}\n") if __name__ == "__main__":