diff --git a/3rd-party b/3rd-party index 1eec092..ac6d28a 160000 --- a/3rd-party +++ b/3rd-party @@ -1 +1 @@ -Subproject commit 1eec0920bb63f7b983a619babe12f1499b0863bb +Subproject commit ac6d28adc0b2e6d3adf1e8a6983dd0d64e757aaa diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f185ee..606332c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -232,12 +232,16 @@ endif() # Sokol ################################ +set(SOKOL_LIB_SRC src/system/sokol_gfx.c) + if(APPLE) - add_library(sokol src/ext/sokol_impl.m) + set(SOKOL_LIB_SRC ${SOKOL_LIB_SRC} src/system/sokol_impl.m) else() - add_library(sokol src/ext/sokol_impl.c) + set(SOKOL_LIB_SRC ${SOKOL_LIB_SRC} src/system/sokol_impl.c) endif() +add_library(sokol ${SOKOL_LIB_SRC}) + if(APPLE) target_compile_definitions(sokol PRIVATE SOKOL_METAL) elseif(WIN32) @@ -246,10 +250,6 @@ elseif(LINUX) target_compile_definitions(sokol PRIVATE SOKOL_GLCORE33) endif() - #set_source_files_properties(${SOKOL_SRC} PROPERTIES COMPILE_FLAGS -DSOKOL_METAL) - # set_source_files_properties(${SOKOL_SRC} PROPERTIES COMPILE_FLAGS "-DSOKOL_D3D11 -DSOKOL_D3D11_SHADER_COMPILER") - #set_source_files_properties(${SOKOL_SRC} PROPERTIES COMPILE_FLAGS -DSOKOL_GLCORE33) - if(APPLE) set_property (TARGET sokol APPEND_STRING PROPERTY COMPILE_FLAGS "-fobjc-arc") @@ -292,15 +292,6 @@ if(MINGW) target_link_libraries(sokol-renderer mingw32) endif() -# TODO: remove this and define shaders in the sokol lib -if(APPLE) - target_compile_definitions(sokol-renderer PRIVATE SOKOL_METAL) -elseif(WIN32) - target_compile_definitions(sokol-renderer PRIVATE SOKOL_D3D11) -elseif(LINUX) - target_compile_definitions(sokol-renderer PRIVATE SOKOL_GLCORE33) -endif() - target_include_directories(sokol-renderer PRIVATE include) target_include_directories(sokol-renderer PRIVATE 3rd-party/sokol) target_include_directories(sokol-renderer PRIVATE src) @@ -493,27 +484,15 @@ target_compile_definitions(tic80prolib PRIVATE TIC80_PRO) # TIC-80 app ################################ -set(TIC80_OUTPUTS ${TIC80_OUTPUTS} tic80-sokol tic80pro-sokol) - foreach(TIC80_OUTPUT ${TIC80_OUTPUTS}) - if(TIC80_OUTPUT MATCHES "-sokol") - set(SOKOL_RENDERER TRUE) - else() - set(SOKOL_RENDERER FALSE) - endif() - set(TIC80_SRC src/ext/file_dialog.c) if(APPLE) set(TIC80_SRC ${TIC80_SRC} src/ext/file_dialog.m) endif() - if(SOKOL_RENDERER) - set(TIC80_SRC ${TIC80_SRC} src/system/sokol.c) - else() - set(TIC80_SRC ${TIC80_SRC} src/net.c src/system/sdlgpu.c) - endif() + set(TIC80_SRC ${TIC80_SRC} src/net.c src/system/sdlgpu.c) if(WIN32) set(TIC80_SRC ${TIC80_SRC} build/windows/tic80.rc) @@ -531,39 +510,20 @@ foreach(TIC80_OUTPUT ${TIC80_OUTPUTS}) target_include_directories(${TIC80_OUTPUT} PRIVATE 3rd-party/SDL2_net-2.0.1) target_include_directories(${TIC80_OUTPUT} PRIVATE 3rd-party/SDL2-2.0.8/include) - if(SOKOL_RENDERER) - target_include_directories(${TIC80_OUTPUT} PRIVATE 3rd-party/sokol) - else() - target_include_directories(${TIC80_OUTPUT} PRIVATE 3rd-party/sdl-gpu/include) - endif() + target_include_directories(${TIC80_OUTPUT} PRIVATE 3rd-party/sdl-gpu/include) if(MINGW) target_link_libraries(${TIC80_OUTPUT} mingw32) endif() - if(SOKOL_RENDERER) - # TODO fix Pro tic80lib - add_dependencies(${TIC80_OUTPUT} tic80lib sokol) - target_link_libraries(${TIC80_OUTPUT} tic80lib sokol) - - if(APPLE) - target_compile_definitions(${TIC80_OUTPUT} PRIVATE SOKOL_METAL) - elseif(WIN32) - target_compile_definitions(${TIC80_OUTPUT} PRIVATE SOKOL_D3D11) - elseif(LINUX) - target_compile_definitions(${TIC80_OUTPUT} PRIVATE SOKOL_GLCORE33) - endif() - - else() - if(NOT EMSCRIPTEN) - add_dependencies(${TIC80_OUTPUT} SDL2main SDL2-static) - target_link_libraries(${TIC80_OUTPUT} SDL2-static SDL2main) - endif() - - add_dependencies(${TIC80_OUTPUT} ${TIC80_OUTPUT}lib sdlnet sdlgpu) - target_link_libraries(${TIC80_OUTPUT} ${TIC80_OUTPUT}lib sdlnet sdlgpu) + if(NOT EMSCRIPTEN) + add_dependencies(${TIC80_OUTPUT} SDL2main SDL2-static) + target_link_libraries(${TIC80_OUTPUT} SDL2-static SDL2main) endif() + add_dependencies(${TIC80_OUTPUT} ${TIC80_OUTPUT}lib sdlnet sdlgpu) + target_link_libraries(${TIC80_OUTPUT} ${TIC80_OUTPUT}lib sdlnet sdlgpu) + if(LINUX) include(FindPkgConfig) if(NOT PKG_CONFIG_FOUND) @@ -577,3 +537,57 @@ foreach(TIC80_OUTPUT ${TIC80_OUTPUTS}) endif() endforeach() + + +################################ +# TIC-80 app (Sokol) +################################ + +foreach(TIC80_OUTPUT ${TIC80_OUTPUTS}) + + set(TIC80_SRC src/ext/file_dialog.c) + + if(APPLE) + set(TIC80_SRC ${TIC80_SRC} src/ext/file_dialog.m) + endif() + + set(TIC80_SRC ${TIC80_SRC} src/system/sokol.c) + + if(WIN32) + set(TIC80_SRC ${TIC80_SRC} build/windows/tic80.rc) + add_executable(${TIC80_OUTPUT}-sokol WIN32 ${TIC80_SRC}) + elseif(APPLE) + add_executable(${TIC80_OUTPUT} MACOSX_BUNDLE ${TIC80_SRC} build/macosx/tic80.icns) + set_source_files_properties(build/macosx/tic80.icns PROPERTIES MACOSX_PACKAGE_LOCATION RESOURCES) + set_target_properties(${TIC80_OUTPUT}-sokol PROPERTIES MACOSX_BUNDLE_INFO_PLIST build/macosx/${TIC80_OUTPUT}-sokol.plist) + else() + add_executable(${TIC80_OUTPUT}-sokol ${TIC80_SRC}) + endif() + + target_include_directories(${TIC80_OUTPUT}-sokol PRIVATE include) + target_include_directories(${TIC80_OUTPUT}-sokol PRIVATE src) + target_include_directories(${TIC80_OUTPUT}-sokol PRIVATE 3rd-party/SDL2_net-2.0.1) + target_include_directories(${TIC80_OUTPUT}-sokol PRIVATE 3rd-party/SDL2-2.0.8/include) + + target_include_directories(${TIC80_OUTPUT}-sokol PRIVATE 3rd-party/sokol) + + if(MINGW) + target_link_libraries(${TIC80_OUTPUT}-sokol mingw32) + endif() + + add_dependencies(${TIC80_OUTPUT}-sokol ${TIC80_OUTPUT}lib sokol) + target_link_libraries(${TIC80_OUTPUT}-sokol ${TIC80_OUTPUT}lib sokol) + + if(LINUX) + include(FindPkgConfig) + if(NOT PKG_CONFIG_FOUND) + message(FATAL_ERROR "We need pkg-config to compile this project") + endif() + + pkg_check_modules(GTK REQUIRED gtk+-3.0) + + target_include_directories(${TIC80_OUTPUT}-sokol PRIVATE ${GTK_INCLUDE_DIRS}) + target_link_libraries(${TIC80_OUTPUT}-sokol ${GTK_LIBRARIES}) + endif() + +endforeach() diff --git a/examples/sokol/main.c b/examples/sokol/main.c index d8e3cb4..0da7659 100644 --- a/examples/sokol/main.c +++ b/examples/sokol/main.c @@ -1,7 +1,4 @@ -#include "sokol_app.h" -#include "sokol_gfx.h" -#include "sokol_time.h" -#include "sokol_audio.h" +#include "system/sokol.h" #include #include @@ -12,247 +9,6 @@ static tic80* tic = NULL; -#if defined(SOKOL_GLCORE33) -static const char* gfx_vs_src = - "#version 330\n" - "in vec2 in_pos;\n" - "in vec2 in_uv;\n" - "out vec2 uv;\n" - "void main() {\n" - " gl_Position = vec4(in_pos*2.0-1.0, 0.5, 1.0);\n" - " uv = in_uv;\n" - "}\n"; -static const char* gfx_fs_src = - "#version 330\n" - "uniform sampler2D tex;\n" - "in vec2 uv;\n" - "out vec4 frag_color;\n" - "void main() {\n" - " frag_color = texture(tex, uv);\n" - "}\n"; -#elif defined(SOKOL_GLES2) -static const char* gfx_vs_src = - "attribute vec2 in_pos;\n" - "attribute vec2 in_uv;\n" - "varying vec2 uv;\n" - "void main() {\n" - " gl_Position = vec4(in_pos*2.0-1.0, 0.5, 1.0);\n" - " uv = in_uv;\n" - "}\n"; -static const char* gfx_fs_src = - "precision mediump float;\n" - "uniform sampler2D tex;" - "varying vec2 uv;\n" - "void main() {\n" - " gl_FragColor = texture2D(tex, uv);\n" - "}\n"; -#elif defined(SOKOL_METAL) -static const char* gfx_vs_src = - "#include \n" - "using namespace metal;\n" - "struct vs_in {\n" - " float2 pos [[attribute(0)]];\n" - " float2 uv [[attribute(1)]];\n" - "};\n" - "struct vs_out {\n" - " float4 pos [[position]];\n" - " float2 uv;\n" - "};\n" - "vertex vs_out _main(vs_in in [[stage_in]]) {\n" - " vs_out out;\n" - " out.pos = float4(in.pos*2.0-1.0, 0.5, 1.0);\n" - " out.uv = in.uv;\n" - " return out;\n" - "}\n"; -static const char* gfx_fs_src = - "#include \n" - "using namespace metal;\n" - "struct fs_in {\n" - " float2 uv;\n" - "};\n" - "fragment float4 _main(fs_in in [[stage_in]], texture2d tex [[texture(0)]], sampler smp [[sampler(0)]]) {\n" - " return tex.sample(smp, in.uv);\n" - "}\n"; -#elif defined(SOKOL_D3D11) -static const char* gfx_vs_src = - "struct vs_in {\n" - " float2 pos: POS;\n" - " float2 uv: UV;\n" - "};\n" - "struct vs_out {\n" - " float2 uv: TEXCOORD0;\n" - " float4 pos: SV_Position;\n" - "};\n" - "vs_out main(vs_in inp) {\n" - " vs_out outp;\n" - " outp.pos = float4(inp.pos*2.0-1.0, 0.5, 1.0);\n" - " outp.uv = inp.uv;\n" - " return outp;\n" - "}\n"; -static const char* gfx_fs_src = - "Texture2D tex: register(t0);\n" - "sampler smp: register(s0);\n" - "float4 main(float2 uv: TEXCOORD0): SV_Target0 {\n" - " return tex.Sample(smp, uv);\n" - "}\n"; -#endif - -typedef struct { - sg_draw_state upscale_draw_state; - sg_pass upscale_pass; - sg_draw_state draw_state; - int fb_width; - int fb_height; - int fb_aspect_scale_x; - int fb_aspect_scale_y; -} gfx_state; - -static gfx_state gfx; - -void gfx_init(int w, int h, int sx, int sy) { - gfx.fb_width = w; - gfx.fb_height = h; - gfx.fb_aspect_scale_x = sx; - gfx.fb_aspect_scale_y = sy; - sg_setup(&(sg_desc){ - .mtl_device = sapp_metal_get_device(), - .mtl_renderpass_descriptor_cb = sapp_metal_get_renderpass_descriptor, - .mtl_drawable_cb = sapp_metal_get_drawable, - .d3d11_device = sapp_d3d11_get_device(), - .d3d11_device_context = sapp_d3d11_get_device_context(), - .d3d11_render_target_view_cb = sapp_d3d11_get_render_target_view, - .d3d11_depth_stencil_view_cb = sapp_d3d11_get_depth_stencil_view - }); - - /* quad vertex buffers with and without flipped UVs */ - float verts[] = { - 0.0f, 0.0f, 0.0f, 0.0f, - 1.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 1.0f, - 1.0f, 1.0f, 1.0f, 1.0f - }; - float verts_flipped[] = { - 0.0f, 0.0f, 0.0f, 1.0f, - 1.0f, 0.0f, 1.0f, 1.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 1.0f, 1.0f, 1.0f, 0.0f - }; - gfx.upscale_draw_state.vertex_buffers[0] = sg_make_buffer(&(sg_buffer_desc){ - .size = sizeof(verts), - .content = verts, - }); - gfx.draw_state.vertex_buffers[0] = sg_make_buffer(&(sg_buffer_desc){ - .size = sizeof(verts), - .content = sg_query_feature(SG_FEATURE_ORIGIN_TOP_LEFT) ? verts : verts_flipped - }); - - /* a shader to render a textured quad */ - sg_shader fsq_shd = sg_make_shader(&(sg_shader_desc){ - .fs.images = { - [0] = { .name="tex", .type=SG_IMAGETYPE_2D }, - }, - .vs.source = gfx_vs_src, - .fs.source = gfx_fs_src, - }); - - /* 2 pipeline-state-objects, one for upscaling, one for rendering */ - sg_pipeline_desc pip_desc = { - .layout = { - .attrs[0] = { .name="in_pos", .sem_name="POS", .format=SG_VERTEXFORMAT_FLOAT2 }, - .attrs[1] = { .name="in_uv", .sem_name="UV", .format=SG_VERTEXFORMAT_FLOAT2 } - }, - .shader = fsq_shd, - .primitive_type = SG_PRIMITIVETYPE_TRIANGLE_STRIP - }; - gfx.draw_state.pipeline = sg_make_pipeline(&pip_desc); - pip_desc.blend.depth_format = SG_PIXELFORMAT_NONE; - gfx.upscale_draw_state.pipeline = sg_make_pipeline(&pip_desc); - - /* a texture with the emulator's raw pixel data */ - gfx.upscale_draw_state.fs_images[0] = sg_make_image(&(sg_image_desc){ - .width = gfx.fb_width, - .height = gfx.fb_height, - .pixel_format = SG_PIXELFORMAT_RGBA8, - .usage = SG_USAGE_STREAM, - .min_filter = SG_FILTER_NEAREST, - .mag_filter = SG_FILTER_NEAREST, - .wrap_u = SG_WRAP_CLAMP_TO_EDGE, - .wrap_v = SG_WRAP_CLAMP_TO_EDGE - }); - /* a 2x upscaled render-target-texture */ - gfx.draw_state.fs_images[0] = sg_make_image(&(sg_image_desc){ - .render_target = true, - .width = 2*gfx.fb_width, - .height = 2*gfx.fb_height, - .pixel_format = SG_PIXELFORMAT_RGBA8, - .min_filter = SG_FILTER_LINEAR, - .mag_filter = SG_FILTER_LINEAR, - .wrap_u = SG_WRAP_CLAMP_TO_EDGE, - .wrap_v = SG_WRAP_CLAMP_TO_EDGE - }); - - /* a render pass for the 2x upscaling */ - gfx.upscale_pass = sg_make_pass(&(sg_pass_desc){ - .color_attachments[0].image = gfx.draw_state.fs_images[0] - }); -} - -static const sg_pass_action gfx_upscale_pass_action = { - .colors[0] = { .action = SG_ACTION_DONTCARE } -}; - -static const sg_pass_action gfx_draw_pass_action = { - .colors[0] = { .action = SG_ACTION_CLEAR, .val = { 0.05f, 0.05f, 0.05f, 1.0f } } -}; - -static void apply_viewport(void) { - const int canvas_width = sapp_width(); - const int canvas_height = sapp_height(); - const float canvas_aspect = (float)canvas_width / (float)canvas_height; - const float fb_aspect = (float)(gfx.fb_width*gfx.fb_aspect_scale_x) / (float)(gfx.fb_height*gfx.fb_aspect_scale_y); - const int frame_x = 5; - const int frame_y = 5; - int vp_x, vp_y, vp_w, vp_h; - if (fb_aspect < canvas_aspect) { - vp_y = frame_y; - vp_h = canvas_height - 2 * frame_y; - vp_w = (int) (canvas_height * fb_aspect) - 2 * frame_x; - vp_x = (canvas_width - vp_w) / 2; - } - else { - vp_x = frame_x; - vp_w = canvas_width - 2 * frame_x; - vp_h = (int) (canvas_width / fb_aspect) - 2 * frame_y; - vp_y = frame_y; - } - sg_apply_viewport(vp_x, vp_y, vp_w, vp_h, true); -} - -static void gfx_draw() { - - /* copy emulator pixel data into upscaling source texture */ - sg_update_image(gfx.upscale_draw_state.fs_images[0], &(sg_image_content){ - .subimage[0][0] = { - .ptr = tic->screen, - .size = gfx.fb_width*gfx.fb_height*sizeof(uint32_t) - } - }); - - /* upscale the original framebuffer 2x with nearest filtering */ - sg_begin_pass(gfx.upscale_pass, &gfx_upscale_pass_action); - sg_apply_draw_state(&gfx.upscale_draw_state); - sg_draw(0, 4, 1); - sg_end_pass(); - - /* draw the final pass with linear filtering */ - sg_begin_default_pass(&gfx_draw_pass_action, sapp_width(), sapp_height()); - apply_viewport(); - sg_apply_draw_state(&gfx.draw_state); - sg_draw(0, 4, 1); - sg_end_pass(); - sg_commit(); -} - static void app_init(void) { saudio_desc desc = {0}; @@ -284,7 +40,7 @@ static void app_init(void) } } - gfx_init(TIC80_FULLWIDTH, TIC80_FULLHEIGHT, 1, 1); + sokol_gfx_init(TIC80_FULLWIDTH, TIC80_FULLHEIGHT, 1, 1); } static tic80_input tic_input; @@ -294,7 +50,7 @@ static void app_frame(void) if(tic) tic80_tick(tic, tic_input); - gfx_draw(); + sokol_gfx_draw(tic->screen); static float floatSamples[44100 / 60 * 2]; diff --git a/src/system/sokol.c b/src/system/sokol.c index 3c39790..3d3aefb 100644 --- a/src/system/sokol.c +++ b/src/system/sokol.c @@ -1,14 +1,10 @@ -#include -#include -#include -#include - #include #include #include #include #include "system.h" +#include "system/sokol.h" static struct { @@ -20,247 +16,8 @@ static struct float* samples; } audio; - struct { - sg_draw_state upscale_draw_state; - sg_pass upscale_pass; - sg_draw_state draw_state; - s32 fb_width; - s32 fb_height; - s32 fb_aspect_scale_x; - s32 fb_aspect_scale_y; - } gfx; - } platform; -#if defined(SOKOL_GLCORE33) -static const char* gfx_vs_src = - "#version 330\n" - "in vec2 in_pos;\n" - "in vec2 in_uv;\n" - "out vec2 uv;\n" - "void main() {\n" - " gl_Position = vec4(in_pos*2.0-1.0, 0.5, 1.0);\n" - " uv = in_uv;\n" - "}\n"; -static const char* gfx_fs_src = - "#version 330\n" - "uniform sampler2D tex;\n" - "in vec2 uv;\n" - "out vec4 frag_color;\n" - "void main() {\n" - " frag_color = texture(tex, uv);\n" - "}\n"; -#elif defined(SOKOL_GLES2) -static const char* gfx_vs_src = - "attribute vec2 in_pos;\n" - "attribute vec2 in_uv;\n" - "varying vec2 uv;\n" - "void main() {\n" - " gl_Position = vec4(in_pos*2.0-1.0, 0.5, 1.0);\n" - " uv = in_uv;\n" - "}\n"; -static const char* gfx_fs_src = - "precision mediump float;\n" - "uniform sampler2D tex;" - "varying vec2 uv;\n" - "void main() {\n" - " gl_FragColor = texture2D(tex, uv);\n" - "}\n"; -#elif defined(SOKOL_METAL) -static const char* gfx_vs_src = - "#include \n" - "using namespace metal;\n" - "struct vs_in {\n" - " float2 pos [[attribute(0)]];\n" - " float2 uv [[attribute(1)]];\n" - "};\n" - "struct vs_out {\n" - " float4 pos [[position]];\n" - " float2 uv;\n" - "};\n" - "vertex vs_out _main(vs_in in [[stage_in]]) {\n" - " vs_out out;\n" - " out.pos = float4(in.pos*2.0-1.0, 0.5, 1.0);\n" - " out.uv = in.uv;\n" - " return out;\n" - "}\n"; -static const char* gfx_fs_src = - "#include \n" - "using namespace metal;\n" - "struct fs_in {\n" - " float2 uv;\n" - "};\n" - "fragment float4 _main(fs_in in [[stage_in]], texture2d tex [[texture(0)]], sampler smp [[sampler(0)]]) {\n" - " return tex.sample(smp, in.uv);\n" - "}\n"; -#elif defined(SOKOL_D3D11) -static const char* gfx_vs_src = - "struct vs_in {\n" - " float2 pos: POS;\n" - " float2 uv: UV;\n" - "};\n" - "struct vs_out {\n" - " float2 uv: TEXCOORD0;\n" - " float4 pos: SV_Position;\n" - "};\n" - "vs_out main(vs_in inp) {\n" - " vs_out outp;\n" - " outp.pos = float4(inp.pos*2.0-1.0, 0.5, 1.0);\n" - " outp.uv = inp.uv;\n" - " return outp;\n" - "}\n"; -static const char* gfx_fs_src = - "Texture2D tex: register(t0);\n" - "sampler smp: register(s0);\n" - "float4 main(float2 uv: TEXCOORD0): SV_Target0 {\n" - " return tex.Sample(smp, uv);\n" - "}\n"; -#endif - -void gfx_init(s32 w, s32 h, s32 sx, s32 sy) { - platform.gfx.fb_width = w; - platform.gfx.fb_height = h; - platform.gfx.fb_aspect_scale_x = sx; - platform.gfx.fb_aspect_scale_y = sy; - sg_setup(&(sg_desc){ - .mtl_device = sapp_metal_get_device(), - .mtl_renderpass_descriptor_cb = sapp_metal_get_renderpass_descriptor, - .mtl_drawable_cb = sapp_metal_get_drawable, - .d3d11_device = sapp_d3d11_get_device(), - .d3d11_device_context = sapp_d3d11_get_device_context(), - .d3d11_render_target_view_cb = sapp_d3d11_get_render_target_view, - .d3d11_depth_stencil_view_cb = sapp_d3d11_get_depth_stencil_view - }); - - /* quad vertex buffers with and without flipped UVs */ - float verts[] = { - 0.0f, 0.0f, 0.0f, 0.0f, - 1.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 1.0f, - 1.0f, 1.0f, 1.0f, 1.0f - }; - float verts_flipped[] = { - 0.0f, 0.0f, 0.0f, 1.0f, - 1.0f, 0.0f, 1.0f, 1.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 1.0f, 1.0f, 1.0f, 0.0f - }; - platform.gfx.upscale_draw_state.vertex_buffers[0] = sg_make_buffer(&(sg_buffer_desc){ - .size = sizeof(verts), - .content = verts, - }); - platform.gfx.draw_state.vertex_buffers[0] = sg_make_buffer(&(sg_buffer_desc){ - .size = sizeof(verts), - .content = sg_query_feature(SG_FEATURE_ORIGIN_TOP_LEFT) ? verts : verts_flipped - }); - - /* a shader to render a textured quad */ - sg_shader fsq_shd = sg_make_shader(&(sg_shader_desc){ - .fs.images = { - [0] = { .name="tex", .type=SG_IMAGETYPE_2D }, - }, - .vs.source = gfx_vs_src, - .fs.source = gfx_fs_src, - }); - - /* 2 pipeline-state-objects, one for upscaling, one for rendering */ - sg_pipeline_desc pip_desc = { - .layout = { - .attrs[0] = { .name="in_pos", .sem_name="POS", .format=SG_VERTEXFORMAT_FLOAT2 }, - .attrs[1] = { .name="in_uv", .sem_name="UV", .format=SG_VERTEXFORMAT_FLOAT2 } - }, - .shader = fsq_shd, - .primitive_type = SG_PRIMITIVETYPE_TRIANGLE_STRIP - }; - platform.gfx.draw_state.pipeline = sg_make_pipeline(&pip_desc); - pip_desc.blend.depth_format = SG_PIXELFORMAT_NONE; - platform.gfx.upscale_draw_state.pipeline = sg_make_pipeline(&pip_desc); - - /* a texture with the emulator's raw pixel data */ - platform.gfx.upscale_draw_state.fs_images[0] = sg_make_image(&(sg_image_desc){ - .width = platform.gfx.fb_width, - .height = platform.gfx.fb_height, - .pixel_format = SG_PIXELFORMAT_RGBA8, - .usage = SG_USAGE_STREAM, - .min_filter = SG_FILTER_NEAREST, - .mag_filter = SG_FILTER_NEAREST, - .wrap_u = SG_WRAP_CLAMP_TO_EDGE, - .wrap_v = SG_WRAP_CLAMP_TO_EDGE - }); - /* a 2x upscaled render-target-texture */ - platform.gfx.draw_state.fs_images[0] = sg_make_image(&(sg_image_desc){ - .render_target = true, - .width = 2*platform.gfx.fb_width, - .height = 2*platform.gfx.fb_height, - .pixel_format = SG_PIXELFORMAT_RGBA8, - .min_filter = SG_FILTER_LINEAR, - .mag_filter = SG_FILTER_LINEAR, - .wrap_u = SG_WRAP_CLAMP_TO_EDGE, - .wrap_v = SG_WRAP_CLAMP_TO_EDGE - }); - - /* a render pass for the 2x upscaling */ - platform.gfx.upscale_pass = sg_make_pass(&(sg_pass_desc){ - .color_attachments[0].image = platform.gfx.draw_state.fs_images[0] - }); -} - -static const sg_pass_action gfx_upscale_pass_action = { - .colors[0] = { .action = SG_ACTION_DONTCARE } -}; - -static const sg_pass_action gfx_draw_pass_action = { - .colors[0] = { .action = SG_ACTION_CLEAR, .val = { 0.05f, 0.05f, 0.05f, 1.0f } } -}; - -static void apply_viewport(void) { - const s32 canvas_width = sapp_width(); - const s32 canvas_height = sapp_height(); - const float canvas_aspect = (float)canvas_width / (float)canvas_height; - const float fb_aspect = (float)(platform.gfx.fb_width*platform.gfx.fb_aspect_scale_x) / (float)(platform.gfx.fb_height*platform.gfx.fb_aspect_scale_y); - const s32 frame_x = 5; - const s32 frame_y = 5; - s32 vp_x, vp_y, vp_w, vp_h; - if (fb_aspect < canvas_aspect) { - vp_y = frame_y; - vp_h = canvas_height - 2 * frame_y; - vp_w = (s32) (canvas_height * fb_aspect) - 2 * frame_x; - vp_x = (canvas_width - vp_w) / 2; - } - else { - vp_x = frame_x; - vp_w = canvas_width - 2 * frame_x; - vp_h = (s32) (canvas_width / fb_aspect) - 2 * frame_y; - vp_y = frame_y; - } - sg_apply_viewport(vp_x, vp_y, vp_w, vp_h, true); -} - -static void gfx_draw() { - - /* copy emulator pixel data into upscaling source texture */ - sg_update_image(platform.gfx.upscale_draw_state.fs_images[0], &(sg_image_content){ - .subimage[0][0] = { - .ptr = platform.studio->tic->screen, - .size = platform.gfx.fb_width*platform.gfx.fb_height*sizeof platform.studio->tic->screen[0] - } - }); - - /* upscale the original framebuffer 2x with nearest filtering */ - sg_begin_pass(platform.gfx.upscale_pass, &gfx_upscale_pass_action); - sg_apply_draw_state(&platform.gfx.upscale_draw_state); - sg_draw(0, 4, 1); - sg_end_pass(); - - /* draw the final pass with linear filtering */ - sg_begin_default_pass(&gfx_draw_pass_action, sapp_width(), sapp_height()); - apply_viewport(); - sg_apply_draw_state(&platform.gfx.draw_state); - sg_draw(0, 4, 1); - sg_end_pass(); - sg_commit(); -} - static void setClipboardText(const char* text) { } @@ -359,7 +116,7 @@ static System systemInterface = static void app_init(void) { - gfx_init(TIC80_FULLWIDTH, TIC80_FULLHEIGHT, 1, 1); + sokol_gfx_init(TIC80_FULLWIDTH, TIC80_FULLHEIGHT, 1, 1); platform.audio.samples = calloc(sizeof platform.audio.samples[0], saudio_sample_rate() / TIC_FRAMERATE * TIC_STEREO_CHANNLES); } @@ -375,7 +132,7 @@ static void app_frame(void) platform.studio->tick(); - gfx_draw(); + sokol_gfx_draw(platform.studio->tic->screen); s32 count = tic->samples.size / sizeof tic->samples.buffer[0]; for(s32 i = 0; i < count; i++) diff --git a/src/system/sokol.h b/src/system/sokol.h new file mode 100644 index 0000000..5675dab --- /dev/null +++ b/src/system/sokol.h @@ -0,0 +1,11 @@ +#pragma once + +#include "sokol_app.h" +#include "sokol_gfx.h" +#include "sokol_time.h" +#include "sokol_audio.h" + +#include + +void sokol_gfx_init(int w, int h, int sx, int sy); +void sokol_gfx_draw(const uint32_t* ptr); diff --git a/src/system/sokol_gfx.c b/src/system/sokol_gfx.c new file mode 100644 index 0000000..bb78bca --- /dev/null +++ b/src/system/sokol_gfx.c @@ -0,0 +1,246 @@ +#include "sokol.h" + +static struct +{ + sg_draw_state upscale_draw_state; + sg_pass upscale_pass; + sg_draw_state draw_state; + int fb_width; + int fb_height; + int fb_aspect_scale_x; + int fb_aspect_scale_y; +} sokol_gfx; + +#if defined(SOKOL_GLCORE33) +static const char* gfx_vs_src = + "#version 330\n" + "in vec2 in_pos;\n" + "in vec2 in_uv;\n" + "out vec2 uv;\n" + "void main() {\n" + " gl_Position = vec4(in_pos*2.0-1.0, 0.5, 1.0);\n" + " uv = in_uv;\n" + "}\n"; +static const char* gfx_fs_src = + "#version 330\n" + "uniform sampler2D tex;\n" + "in vec2 uv;\n" + "out vec4 frag_color;\n" + "void main() {\n" + " frag_color = texture(tex, uv);\n" + "}\n"; +#elif defined(SOKOL_GLES2) +static const char* gfx_vs_src = + "attribute vec2 in_pos;\n" + "attribute vec2 in_uv;\n" + "varying vec2 uv;\n" + "void main() {\n" + " gl_Position = vec4(in_pos*2.0-1.0, 0.5, 1.0);\n" + " uv = in_uv;\n" + "}\n"; +static const char* gfx_fs_src = + "precision mediump float;\n" + "uniform sampler2D tex;" + "varying vec2 uv;\n" + "void main() {\n" + " gl_FragColor = texture2D(tex, uv);\n" + "}\n"; +#elif defined(SOKOL_METAL) +static const char* gfx_vs_src = + "#include \n" + "using namespace metal;\n" + "struct vs_in {\n" + " float2 pos [[attribute(0)]];\n" + " float2 uv [[attribute(1)]];\n" + "};\n" + "struct vs_out {\n" + " float4 pos [[position]];\n" + " float2 uv;\n" + "};\n" + "vertex vs_out _main(vs_in in [[stage_in]]) {\n" + " vs_out out;\n" + " out.pos = float4(in.pos*2.0-1.0, 0.5, 1.0);\n" + " out.uv = in.uv;\n" + " return out;\n" + "}\n"; +static const char* gfx_fs_src = + "#include \n" + "using namespace metal;\n" + "struct fs_in {\n" + " float2 uv;\n" + "};\n" + "fragment float4 _main(fs_in in [[stage_in]], texture2d tex [[texture(0)]], sampler smp [[sampler(0)]]) {\n" + " return tex.sample(smp, in.uv);\n" + "}\n"; +#elif defined(SOKOL_D3D11) +static const char* gfx_vs_src = + "struct vs_in {\n" + " float2 pos: POS;\n" + " float2 uv: UV;\n" + "};\n" + "struct vs_out {\n" + " float2 uv: TEXCOORD0;\n" + " float4 pos: SV_Position;\n" + "};\n" + "vs_out main(vs_in inp) {\n" + " vs_out outp;\n" + " outp.pos = float4(inp.pos*2.0-1.0, 0.5, 1.0);\n" + " outp.uv = inp.uv;\n" + " return outp;\n" + "}\n"; +static const char* gfx_fs_src = + "Texture2D tex: register(t0);\n" + "sampler smp: register(s0);\n" + "float4 main(float2 uv: TEXCOORD0): SV_Target0 {\n" + " return tex.Sample(smp, uv);\n" + "}\n"; +#endif + +void sokol_gfx_init(int w, int h, int sx, int sy) { + sokol_gfx.fb_width = w; + sokol_gfx.fb_height = h; + sokol_gfx.fb_aspect_scale_x = sx; + sokol_gfx.fb_aspect_scale_y = sy; + sg_setup(&(sg_desc){ + .mtl_device = sapp_metal_get_device(), + .mtl_renderpass_descriptor_cb = sapp_metal_get_renderpass_descriptor, + .mtl_drawable_cb = sapp_metal_get_drawable, + .d3d11_device = sapp_d3d11_get_device(), + .d3d11_device_context = sapp_d3d11_get_device_context(), + .d3d11_render_target_view_cb = sapp_d3d11_get_render_target_view, + .d3d11_depth_stencil_view_cb = sapp_d3d11_get_depth_stencil_view + }); + + /* quad vertex buffers with and without flipped UVs */ + float verts[] = { + 0.0f, 0.0f, 0.0f, 0.0f, + 1.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 1.0f + }; + float verts_flipped[] = { + 0.0f, 0.0f, 0.0f, 1.0f, + 1.0f, 0.0f, 1.0f, 1.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 1.0f, 1.0f, 1.0f, 0.0f + }; + sokol_gfx.upscale_draw_state.vertex_buffers[0] = sg_make_buffer(&(sg_buffer_desc){ + .size = sizeof(verts), + .content = verts, + }); + sokol_gfx.draw_state.vertex_buffers[0] = sg_make_buffer(&(sg_buffer_desc){ + .size = sizeof(verts), + .content = sg_query_feature(SG_FEATURE_ORIGIN_TOP_LEFT) ? verts : verts_flipped + }); + + /* a shader to render a textured quad */ + sg_shader fsq_shd = sg_make_shader(&(sg_shader_desc){ + .fs.images = { + [0] = { .name="tex", .type=SG_IMAGETYPE_2D }, + }, + .vs.source = gfx_vs_src, + .fs.source = gfx_fs_src, + }); + + /* 2 pipeline-state-objects, one for upscaling, one for rendering */ + sg_pipeline_desc pip_desc = { + .layout = { + .attrs[0] = { .name="in_pos", .sem_name="POS", .format=SG_VERTEXFORMAT_FLOAT2 }, + .attrs[1] = { .name="in_uv", .sem_name="UV", .format=SG_VERTEXFORMAT_FLOAT2 } + }, + .shader = fsq_shd, + .primitive_type = SG_PRIMITIVETYPE_TRIANGLE_STRIP + }; + sokol_gfx.draw_state.pipeline = sg_make_pipeline(&pip_desc); + pip_desc.blend.depth_format = SG_PIXELFORMAT_NONE; + sokol_gfx.upscale_draw_state.pipeline = sg_make_pipeline(&pip_desc); + + /* a texture with the emulator's raw pixel data */ + sokol_gfx.upscale_draw_state.fs_images[0] = sg_make_image(&(sg_image_desc){ + .width = sokol_gfx.fb_width, + .height = sokol_gfx.fb_height, + .pixel_format = SG_PIXELFORMAT_RGBA8, + .usage = SG_USAGE_STREAM, + .min_filter = SG_FILTER_NEAREST, + .mag_filter = SG_FILTER_NEAREST, + .wrap_u = SG_WRAP_CLAMP_TO_EDGE, + .wrap_v = SG_WRAP_CLAMP_TO_EDGE + }); + /* a 2x upscaled render-target-texture */ + sokol_gfx.draw_state.fs_images[0] = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 2*sokol_gfx.fb_width, + .height = 2*sokol_gfx.fb_height, + .pixel_format = SG_PIXELFORMAT_RGBA8, + .min_filter = SG_FILTER_LINEAR, + .mag_filter = SG_FILTER_LINEAR, + .wrap_u = SG_WRAP_CLAMP_TO_EDGE, + .wrap_v = SG_WRAP_CLAMP_TO_EDGE + }); + + /* a render pass for the 2x upscaling */ + sokol_gfx.upscale_pass = sg_make_pass(&(sg_pass_desc){ + .color_attachments[0].image = sokol_gfx.draw_state.fs_images[0] + }); +} + +static const sg_pass_action gfx_upscale_pass_action = { + .colors[0] = { .action = SG_ACTION_DONTCARE } +}; + +static const sg_pass_action gfx_draw_pass_action = { + .colors[0] = { .action = SG_ACTION_CLEAR, .val = { 0.05f, 0.05f, 0.05f, 1.0f } } +}; + +static void apply_viewport(void) { + const int canvas_width = sapp_width(); + const int canvas_height = sapp_height(); + const float canvas_aspect = (float)canvas_width / (float)canvas_height; + const float fb_aspect = (float)(sokol_gfx.fb_width*sokol_gfx.fb_aspect_scale_x) / (float)(sokol_gfx.fb_height*sokol_gfx.fb_aspect_scale_y); + const int frame_x = 5; + const int frame_y = 5; + int vp_x, vp_y, vp_w, vp_h; + if (fb_aspect < canvas_aspect) { + vp_y = frame_y; + vp_h = canvas_height - 2 * frame_y; + vp_w = (int) (canvas_height * fb_aspect) - 2 * frame_x; + vp_x = (canvas_width - vp_w) / 2; + } + else { + vp_x = frame_x; + vp_w = canvas_width - 2 * frame_x; + vp_h = (int) (canvas_width / fb_aspect) - 2 * frame_y; + + // align top + // vp_y = frame_y; + + // align vcenter + vp_y = (canvas_height - vp_h) / 2; + } + sg_apply_viewport(vp_x, vp_y, vp_w, vp_h, true); +} + +void sokol_gfx_draw(const uint32_t* ptr) { + + /* copy emulator pixel data into upscaling source texture */ + sg_update_image(sokol_gfx.upscale_draw_state.fs_images[0], &(sg_image_content){ + .subimage[0][0] = { + .ptr = ptr, + .size = sokol_gfx.fb_width*sokol_gfx.fb_height*sizeof ptr[0] + } + }); + + /* upscale the original framebuffer 2x with nearest filtering */ + sg_begin_pass(sokol_gfx.upscale_pass, &gfx_upscale_pass_action); + sg_apply_draw_state(&sokol_gfx.upscale_draw_state); + sg_draw(0, 4, 1); + sg_end_pass(); + + /* draw the final pass with linear filtering */ + sg_begin_default_pass(&gfx_draw_pass_action, sapp_width(), sapp_height()); + apply_viewport(); + sg_apply_draw_state(&sokol_gfx.draw_state); + sg_draw(0, 4, 1); + sg_end_pass(); + sg_commit(); +} diff --git a/src/ext/sokol_impl.c b/src/system/sokol_impl.c similarity index 100% rename from src/ext/sokol_impl.c rename to src/system/sokol_impl.c diff --git a/src/ext/sokol_impl.m b/src/system/sokol_impl.m similarity index 100% rename from src/ext/sokol_impl.m rename to src/system/sokol_impl.m