mirror of
https://github.com/GayPizzaSpecifications/padlab.git
synced 2025-08-02 21:00:56 +00:00
metal backend functional
This commit is contained in:
parent
8ce1cca1ef
commit
a75e480155
@ -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")
|
||||
|
@ -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()
|
||||
|
55
cmake/MetalHelper.cmake
Normal file
55
cmake/MetalHelper.cmake
Normal file
@ -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()
|
@ -2,14 +2,24 @@ set(SOURCES
|
||||
maths.h
|
||||
draw.h
|
||||
draw_common.c
|
||||
$<$<NOT:$<OR:$<BOOL:${USE_OPENGL}>,$<BOOL:${USE_OPENGL_LEGACY}>>>:draw.c>
|
||||
$<$<NOT:$<OR:$<BOOL:${USE_METAL}>,$<BOOL:${USE_OPENGL}>,$<BOOL:${USE_OPENGL_LEGACY}>>>:draw.c>
|
||||
$<$<BOOL:${USE_METAL}>:draw_metal.m metal_shader_types.h>
|
||||
$<$<BOOL:${USE_OPENGL}>:draw_opengl_core.c>
|
||||
$<$<BOOL:${USE_OPENGL_LEGACY}>: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
|
||||
$<$<BOOL:${USE_OPENGL}>:${CMAKE_CURRENT_BINARY_DIR}>)
|
||||
$<$<OR:$<BOOL:${USE_METAL}>,$<BOOL:${USE_OPENGL}>>:${CMAKE_CURRENT_BINARY_DIR}>)
|
||||
target_link_libraries(${TARGET}
|
||||
$<$<PLATFORM_ID:Windows>:SDL2::SDL2main>
|
||||
SDL2::SDL2
|
||||
$<$<BOOL:${USE_METAL}>:${METAL} ${QUARTZCORE} ${FOUNDATION}>
|
||||
$<$<OR:$<BOOL:${USE_OPENGL}>,$<BOOL:${USE_OPENGL_LEGACY}>>:OpenGL::GL>
|
||||
$<$<BOOL:${USE_OPENGL}>:gl3w>
|
||||
$<$<BOOL:${GNU}>:m>)
|
||||
target_compile_options(${TARGET} PRIVATE
|
||||
$<$<BOOL:${GNU}>:-Wall -Wextra -pedantic -Wno-unused-parameter>)
|
||||
target_compile_definitions(${TARGET} PRIVATE
|
||||
$<$<BOOL:${USE_METAL}>:USE_METAL>
|
||||
$<$<OR:$<BOOL:${USE_OPENGL}>,$<BOOL:${USE_OPENGL_LEGACY}>>:USE_OPENGL>)
|
||||
|
@ -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
|
||||
|
430
src/draw_metal.m
Normal file
430
src/draw_metal.m
Normal file
@ -0,0 +1,430 @@
|
||||
#include "draw.h"
|
||||
#include "metalShader.h"
|
||||
#include "metal_shader_types.h"
|
||||
#include "maths.h"
|
||||
#include <SDL_metal.h>
|
||||
|
||||
#import <Metal/Metal.h>
|
||||
#import <QuartzCore/CAMetalLayer.h>
|
||||
|
||||
|
||||
@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 <MTLBuffer>*)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<MTLDevice> _dev;
|
||||
CAMetalLayer* _layer;
|
||||
id<MTLCommandQueue> _queue;
|
||||
MTLRenderPassDescriptor* _passDesc;
|
||||
id<MTLRenderPipelineState> _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<MTLBuffer> _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<id<MTLDevice>>* devices = MTLCopyAllDevices();
|
||||
for (id<MTLDevice> 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<MTLLibrary> lib = [_dev newLibraryWithData:shaderData error:&booboo];
|
||||
if (!lib)
|
||||
{
|
||||
fprintf(stderr, "Metal shader compilation failed:\n%s\n", [[booboo localizedDescription] UTF8String]);
|
||||
return nil;
|
||||
}
|
||||
id<MTLFunction> vertPrg = [lib newFunctionWithName:@"vertexMain"];
|
||||
id<MTLFunction> 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 <MTLBuffer>*)buf length:(unsigned)length
|
||||
{
|
||||
if (*buf && length <= [*buf length])
|
||||
return;
|
||||
|
||||
printf("mtlBuf %p %lu -> %u\n", (void*)buf, (unsigned long)[*buf length], length);
|
||||
id<MTLBuffer> 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<CAMetalDrawable> rt = [_layer nextDrawable];
|
||||
_passDesc.colorAttachments[0].texture = rt.texture;
|
||||
|
||||
id<MTLCommandBuffer> cmdBuf = [_queue commandBuffer];
|
||||
id<MTLRenderCommandEncoder> 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];
|
||||
}
|
18
src/metal_shader_types.h
Normal file
18
src/metal_shader_types.h
Normal file
@ -0,0 +1,18 @@
|
||||
#ifndef METAL_SHADER_TYPES_H
|
||||
#define METAL_SHADER_TYPES_H
|
||||
|
||||
#include <simd/simd.h>
|
||||
|
||||
typedef enum
|
||||
{
|
||||
ShaderInputIdxVerticies = 0,
|
||||
ShaderInputViewportScale = 1
|
||||
} ShaderInputIdx;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
vector_float2 position;
|
||||
vector_float4 colour;
|
||||
} ShaderVertex;
|
||||
|
||||
#endif//METAL_SHADER_TYPES_H
|
27
src/shader.metal
Normal file
27
src/shader.metal
Normal file
@ -0,0 +1,27 @@
|
||||
#include "metal_shader_types.h"
|
||||
#include <metal_stdlib>
|
||||
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;
|
||||
}
|
@ -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)
|
||||
|
@ -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__":
|
||||
|
Loading…
Reference in New Issue
Block a user