From 007ffbbc5a3bb4710c5b14a30cf9875505c7c7fc Mon Sep 17 00:00:00 2001 From: Vadim Grigoruk Date: Thu, 27 Sep 2018 23:39:08 +0300 Subject: [PATCH] sokol: renderer works with studio --- CMakeLists.txt | 14 +- examples/sokol/main.c | 2 +- src/system/sokol.c | 430 ++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 426 insertions(+), 20 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3490dc1..3f185ee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -542,8 +542,18 @@ foreach(TIC80_OUTPUT ${TIC80_OUTPUTS}) endif() if(SOKOL_RENDERER) - add_dependencies(${TIC80_OUTPUT} sokol) - target_link_libraries(${TIC80_OUTPUT} sokol) + # 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) diff --git a/examples/sokol/main.c b/examples/sokol/main.c index 631f4db..d8e3cb4 100644 --- a/examples/sokol/main.c +++ b/examples/sokol/main.c @@ -228,7 +228,7 @@ static void apply_viewport(void) { sg_apply_viewport(vp_x, vp_y, vp_w, vp_h, true); } -void gfx_draw() { +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){ diff --git a/src/system/sokol.c b/src/system/sokol.c index 28b473b..3c39790 100644 --- a/src/system/sokol.c +++ b/src/system/sokol.c @@ -1,25 +1,421 @@ -#include "sokol_app.h" -#include "sokol_gfx.h" -#include "sokol_time.h" -#include "sokol_audio.h" +#include +#include +#include +#include #include #include -#include +#include #include -#include +#include "system.h" -sapp_desc sokol_main(int argc, char* argv[]) +static struct { - return (sapp_desc) {0}; - // .init_cb = app_init, - // .frame_cb = app_frame, - // .event_cb = app_input, - // .cleanup_cb = app_cleanup, - // .width = 3 * TIC80_FULLWIDTH, - // .height = 3 * TIC80_FULLHEIGHT, - // .window_title = "TIC-80 with Sokol renderer", - // .ios_keyboard_resizes_canvas = true - // }; + Studio* studio; + + struct + { + saudio_desc desc; + 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) +{ +} + +static bool hasClipboardText() +{ + return false; +} + +static char* getClipboardText() +{ + return NULL; +} + +static void freeClipboardText(const char* text) +{ +} + +static u64 getPerformanceCounter() +{ + return 0; +} + +static u64 getPerformanceFrequency() +{ + return 1000; +} + +static void* getUrlRequest(const char* url, s32* size) +{ + return NULL; +} + +static void goFullscreen() +{ +} + +static void showMessageBox(const char* title, const char* message) +{ +} + +static void setWindowTitle(const char* title) +{ +} + +static void openSystemPath(const char* path) +{ + +} + +static void preseed() +{ +#if defined(__TIC_MACOSX__) + srandom(time(NULL)); + random(); +#else + srand(time(NULL)); + rand(); +#endif +} + +static void pollEvent() +{ + +} + +static void updateConfig() +{ + +} + +static System systemInterface = +{ + .setClipboardText = setClipboardText, + .hasClipboardText = hasClipboardText, + .getClipboardText = getClipboardText, + .freeClipboardText = freeClipboardText, + + .getPerformanceCounter = getPerformanceCounter, + .getPerformanceFrequency = getPerformanceFrequency, + + .getUrlRequest = getUrlRequest, + + .fileDialogLoad = file_dialog_load, + .fileDialogSave = file_dialog_save, + + .goFullscreen = goFullscreen, + .showMessageBox = showMessageBox, + .setWindowTitle = setWindowTitle, + + .openSystemPath = openSystemPath, + .preseed = preseed, + .poll = pollEvent, + .updateConfig = updateConfig, +}; + +static void app_init(void) +{ + 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); +} + +static void app_frame(void) +{ + tic_mem* tic = platform.studio->tic; + + if(platform.studio->quit) + { + return; + } + + platform.studio->tick(); + + gfx_draw(); + + s32 count = tic->samples.size / sizeof tic->samples.buffer[0]; + for(s32 i = 0; i < count; i++) + platform.audio.samples[i] = (float)tic->samples.buffer[i] / SHRT_MAX; + + saudio_push(platform.audio.samples, count / 2); +} + +static void app_input(const sapp_event* event) +{ + +} + +static void app_cleanup(void) +{ + platform.studio->close(); + free(platform.audio.samples); +} + +sapp_desc sokol_main(s32 argc, char* argv[]) +{ + memset(&platform, 0, sizeof platform); + + platform.audio.desc.num_channels = TIC_STEREO_CHANNLES; + saudio_setup(&platform.audio.desc); + + platform.studio = studioInit(argc, argv, saudio_sample_rate(), ".", &systemInterface); + + const s32 Width = TIC80_FULLWIDTH * platform.studio->config()->uiScale; + const s32 Height = TIC80_FULLHEIGHT * platform.studio->config()->uiScale; + + return(sapp_desc) + { + .init_cb = app_init, + .frame_cb = app_frame, + .event_cb = app_input, + .cleanup_cb = app_cleanup, + .width = Width, + .height = Height, + .window_title = TIC_TITLE, + .ios_keyboard_resizes_canvas = true + }; }