From 1ac0c7693d9c2c1222bded539f56e09b7d240c8e Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Tue, 12 Sep 2023 05:02:57 +0000 Subject: [PATCH] GPU gif creation --- source/engine/ffi.c | 2 +- source/engine/render.c | 159 +++++-- source/engine/render.h | 2 +- .../engine/thirdparty/sokol/sokol_gfx_ext.h | 403 ++++++++++++++++++ source/engine/yugine.c | 1 + source/scripts/debug.js | 24 +- 6 files changed, 540 insertions(+), 51 deletions(-) create mode 100644 source/engine/thirdparty/sokol/sokol_gfx_ext.h diff --git a/source/engine/ffi.c b/source/engine/ffi.c index f4243b8..1b0393e 100644 --- a/source/engine/ffi.c +++ b/source/engine/ffi.c @@ -1121,7 +1121,7 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv) break; case 131: - gif_rec_start(); + gif_rec_start(480, 320, 4, 8); break; case 132: str = JS_ToCString(js, argv[1]); diff --git a/source/engine/render.c b/source/engine/render.c index 71f33af..a377ea7 100644 --- a/source/engine/render.c +++ b/source/engine/render.c @@ -15,22 +15,65 @@ #include "stb_ds.h" #include "stb_image_resize.h"; #include "resources.h" +#include "yugine.h" #define SOKOL_GFX_IMPL #include "sokol/sokol_gfx.h" +#define SOKOL_GFX_EXT_IMPL +#include "sokol/sokol_gfx_ext.h" + #define MSF_GIF_IMPL #include "msf_gif.h" -static int gif_w = 480, gif_h = 320, cpf = 1, bitDepth = 16; +static struct { + sg_pass pass; + sg_pass_action pa; + sg_pipeline pipe; + sg_bindings bind; + sg_shader shader; + sg_image img; + sg_image depth; +} sg_gif; + + +static int gif_w = 359, gif_h = 320, cpf = 4, gif_depth = 4; static int gif_rec = 0; +static double gif_timer = 0, gif_spf; static char gif_buf[480*320*4]; static char frame_buf[1920*1080*4]; MsfGifState gif_state = {}; -void gif_rec_start() +void gif_rec_start(int w, int h, int cpf, int bitdepth) { + gif_w = w; + gif_h = h; + gif_depth = bitdepth; msf_gif_begin(&gif_state, gif_w, gif_h); + gif_spf = cpf/100.0; gif_rec = 1; + gif_timer = appTime; + + sg_destroy_image(sg_gif.img); + sg_destroy_image(sg_gif.depth); + sg_destroy_pass(sg_gif.pass); + + sg_gif.img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = gif_w, + .height = gif_h, + }); + + sg_gif.depth = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = gif_w, + .height = gif_h, + .pixel_format = SG_PIXELFORMAT_DEPTH_STENCIL + }); + + sg_gif.pass = sg_make_pass(&(sg_pass_desc){ + .color_attachments[0].image = sg_gif.img, + .depth_stencil_attachment.image = sg_gif.depth + }); } void gif_rec_end(char *path) @@ -61,7 +104,6 @@ static void *read_texture_data(sg_image id, void *pixels) YughWarn("NO GL"); return NULL; #endif - } #include "sokol/sokol_app.h" @@ -143,6 +185,7 @@ static struct { sg_image depth_img; } crt_post; + void trace_make_shader(sg_shader_desc *d, sg_shader result, void *data) { if (sg_query_shader_state(result) == SG_RESOURCESTATE_FAILED) @@ -201,6 +244,24 @@ void render_init() { } }); + sg_gif.shader = sg_compile_shader("shaders/postvert.glsl", "shaders/box.glsl", &(sg_shader_desc){ + .fs.images[0] = { + .name = "diffuse_texture", + .image_type = SG_IMAGETYPE_2D, + .sampler_type = SG_SAMPLERTYPE_FLOAT + } + }); + + sg_gif.pipe = sg_make_pipeline(&(sg_pipeline_desc){ + .shader = sg_gif.shader, + .layout = { + .attrs = { + [0].format = SG_VERTEXFORMAT_FLOAT2, + [1].format = SG_VERTEXFORMAT_FLOAT2 + } + } + }); + crt_post.pipe = sg_make_pipeline(&(sg_pipeline_desc){ .shader = crt_post.shader, .layout = { @@ -229,6 +290,11 @@ void render_init() { .depth_stencil_attachment.image = crt_post.depth_img, }); + sg_gif.pass = sg_make_pass(&(sg_pass_desc){ + .color_attachments[0].image = sg_gif.img, + .depth_stencil_attachment.image = sg_gif.depth + }); + float crt_quad[] = { -1, 1, 0, 1, -1, -1, 0, 0, @@ -244,7 +310,6 @@ void render_init() { }); crt_post.bind.fs_images[0] = crt_post.img; - /* sg_image_desc shadow_desc = { .render_target = true, @@ -341,42 +406,8 @@ HMM_Mat4 hudproj = {0.f}; HMM_Vec3 dirl_pos = {4, 100, 20}; -void openglRender(struct window *window) { -/* - HMM_Mat4 model = HMM_M4D(1.f); - float scale = 0.08; - model = HMM_MulM4(model, HMM_Scale((HMM_Vec3){scale,scale,scale})); - - - // Shadow pass - sg_begin_pass(sg_shadow.pass, &sg_shadow.pass_action); - sg_apply_pipeline(sg_shadow.pipe); - - HMM_Mat4 light_proj = HMM_Orthographic_RH_ZO(-100.f, 100.f, -100.f, 100.f, 1.f, 100.f); - HMM_Mat4 light_view = HMM_LookAt_RH(dirl_pos, (HMM_Vec3){0,0,0}, (HMM_Vec3){0,1,0}); - - HMM_Mat4 lsm = HMM_MulM4(light_proj, light_view); - - HMM_Mat4 subo[2]; - subo[0] = lsm; - subo[1] = model; - - sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, SG_RANGE_REF(subo)); - - for (int i = 0; i < arrlen(duck->meshes); i++) { - sg_bindings sbind = {0}; - sbind.vertex_buffers[0] = duck->meshes[i].bind.vertex_buffers[0]; - sbind.index_buffer = duck->meshes[i].bind.index_buffer; - sg_apply_bindings(&sbind); - sg_draw(0,duck->meshes[i].face_count,1); - } - sg_end_pass(); - - draw_model(duck,model, lsm); -*/ - - sg_begin_pass(crt_post.pass, &pass_action); - +void full_2d_pass(struct window *window) +{ //////////// 2D projection cpVect pos = cam_pos(); @@ -410,21 +441,55 @@ void openglRender(struct window *window) { call_nk_gui(); nuke_end(); +} +void full_3d_pass(struct window *window) +{ + HMM_Mat4 model = HMM_M4D(1.f); + float scale = 0.08; + model = HMM_MulM4(model, HMM_Scale((HMM_Vec3){scale,scale,scale})); + + // Shadow pass + sg_begin_pass(sg_shadow.pass, &sg_shadow.pass_action); + sg_apply_pipeline(sg_shadow.pipe); + + HMM_Mat4 light_proj = HMM_Orthographic_RH_ZO(-100.f, 100.f, -100.f, 100.f, 1.f, 100.f); + HMM_Mat4 light_view = HMM_LookAt_RH(dirl_pos, (HMM_Vec3){0,0,0}, (HMM_Vec3){0,1,0}); + + HMM_Mat4 lsm = HMM_MulM4(light_proj, light_view); + + HMM_Mat4 subo[2]; + subo[0] = lsm; + subo[1] = model; + + sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, SG_RANGE_REF(subo)); +} + +void openglRender(struct window *window) { + sg_begin_pass(crt_post.pass, &pass_action); + full_2d_pass(window); sg_end_pass(); + + if (gif_rec && (appTime - gif_timer) > gif_spf) { + sg_begin_pass(sg_gif.pass, &pass_action); + sg_apply_pipeline(sg_gif.pipe); + sg_apply_bindings(&crt_post.bind); + sg_draw(0,6,1); + sg_end_pass(); + + gif_timer = appTime; + //read_texture_data(sg_gif.img, gif_buf); + sg_query_image_pixels(sg_gif.img, gif_buf, gif_w*gif_h*4); + msf_gif_frame(&gif_state, gif_buf, cpf, gif_depth, gif_w * -4); + } + sg_begin_default_pass(&pass_action, window->width, window->height); sg_apply_pipeline(crt_post.pipe); sg_apply_bindings(&crt_post.bind); sg_draw(0,6,1); - if (gif_rec) { - read_texture_data(crt_post.img, frame_buf); -// stbir_resize_uint8(frame_buf, mainwin.width, mainwin.height, 0, gif_buf, gif_w, gif_h, 0, 4); - msf_gif_frame(&gif_state, frame_buf, cpf, 16, gif_w * -4); - } - - sg_end_pass(); + sg_end_pass(); sg_commit(); diff --git a/source/engine/render.h b/source/engine/render.h index 93e5ba7..9ed8baa 100644 --- a/source/engine/render.h +++ b/source/engine/render.h @@ -73,7 +73,7 @@ void add_zoom(float val); sg_shader sg_compile_shader(const char *v, const char *f, sg_shader_desc *d); -void gif_rec_start(); +void gif_rec_start(int w, int h, int cpf, int bitdepth); void gif_rec_end(char *path); struct uv_n { diff --git a/source/engine/thirdparty/sokol/sokol_gfx_ext.h b/source/engine/thirdparty/sokol/sokol_gfx_ext.h new file mode 100644 index 0000000..bf8b737 --- /dev/null +++ b/source/engine/thirdparty/sokol/sokol_gfx_ext.h @@ -0,0 +1,403 @@ +/* +sokol_gfx_ext.h - extensions for sokol_gfx +https://github.com/edubart/sokol_gp +*/ + +#if defined(SOKOL_IMPL) && !defined(SOKOL_GFX_EXT_IMPL) +#define SOKOL_GFX_EXT_IMPL +#endif + +#ifndef SOKOL_GFX_EXT_INCLUDED +#define SOKOL_GFX_EXT_INCLUDED + +#ifndef SOKOL_GFX_INCLUDED +#error "Please include sokol_gfx.h before sokol_gfx_ext.h" +#endif + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +SOKOL_GFX_API_DECL void sg_query_image_pixels(sg_image img_id, void* pixels, int size); +SOKOL_GFX_API_DECL void sg_query_pixels(int x, int y, int w, int h, bool origin_top_left, void *pixels, int size); +SOKOL_GFX_API_DECL void sg_update_texture_filter(sg_image img_id, sg_filter min_filter, sg_filter mag_filter); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // SOKOL_GFX_EXT_INCLUDED + +#ifdef SOKOL_GFX_EXT_IMPL +#ifndef SOKOL_GFX_EXT_IMPL_INCLUDED +#define SOKOL_GFX_EXT_IMPL_INCLUDED + +#ifndef SOKOL_GFX_IMPL_INCLUDED +#error "Please include sokol_gfx.h implementation before sokol_gp.h implementation" +#endif + +#if defined(_SOKOL_ANY_GL) + +static void _sg_gl_query_image_pixels(_sg_image_t* img, void* pixels) { + SOKOL_ASSERT(img->gl.target == GL_TEXTURE_2D); + SOKOL_ASSERT(0 != img->gl.tex[img->cmn.active_slot]); +#if defined(SOKOL_GLCORE33) + _sg_gl_cache_store_texture_binding(0); + _sg_gl_cache_bind_texture(0, img->gl.target, img->gl.tex[img->cmn.active_slot]); + glGetTexImage(img->gl.target, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + _SG_GL_CHECK_ERROR(); + _sg_gl_cache_restore_texture_binding(0); +#else + static GLuint newFbo = 0; + GLuint oldFbo = 0; + if(newFbo == 0) { + glGenFramebuffers(1, &newFbo); + } + glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&oldFbo); + glBindFramebuffer(GL_FRAMEBUFFER, newFbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, img->gl.tex[img->cmn.active_slot], 0); + glReadPixels(0, 0, img->cmn.width, img->cmn.height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + glBindFramebuffer(GL_FRAMEBUFFER, oldFbo); + //glDeleteFramebuffers(1, &newFbo); + _SG_GL_CHECK_ERROR(); +#endif +} + +static void _sg_gl_query_pixels(int x, int y, int w, int h, bool origin_top_left, void *pixels) { + SOKOL_ASSERT(pixels); + GLuint gl_fb; + GLint dims[4]; + glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&gl_fb); + _SG_GL_CHECK_ERROR(); + glGetIntegerv(GL_VIEWPORT, dims); + int cur_height = dims[3]; + y = origin_top_left ? (cur_height - (y+h)) : y; + _SG_GL_CHECK_ERROR(); +#if defined(SOKOL_GLES2) // use NV extension instead + glReadBufferNV(gl_fb == 0 ? GL_BACK : GL_COLOR_ATTACHMENT0); +#else + glReadBuffer(gl_fb == 0 ? GL_BACK : GL_COLOR_ATTACHMENT0); +#endif + _SG_GL_CHECK_ERROR(); + glReadPixels(x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + _SG_GL_CHECK_ERROR(); +} + +static void _sg_gl_update_texture_filter(_sg_image_t* img, sg_filter min_filter, sg_filter mag_filter) { + _sg_gl_cache_store_texture_binding(0); + _sg_gl_cache_bind_texture(0, img->gl.target, img->gl.tex[img->cmn.active_slot]); + img->cmn.min_filter = min_filter; + img->cmn.mag_filter = mag_filter; + GLenum gl_min_filter = _sg_gl_filter(img->cmn.min_filter); + GLenum gl_mag_filter = _sg_gl_filter(img->cmn.mag_filter); + glTexParameteri(img->gl.target, GL_TEXTURE_MIN_FILTER, (GLint)gl_min_filter); + glTexParameteri(img->gl.target, GL_TEXTURE_MAG_FILTER, (GLint)gl_mag_filter); + _sg_gl_cache_restore_texture_binding(0); +} + +#elif defined(SOKOL_D3D11) + +static inline void _sgext_d3d11_Texture2D_GetDesc(ID3D11Texture2D* self, D3D11_TEXTURE2D_DESC* pDesc) { + #if defined(__cplusplus) + self->GetDesc(pDesc); + #else + self->lpVtbl->GetDesc(self, pDesc); + #endif +} + +static inline void _sgext_d3d11_SamplerState_GetDesc(ID3D11SamplerState* self, D3D11_SAMPLER_DESC* pDesc) { + #if defined(__cplusplus) + self->GetDesc(pDesc); + #else + self->lpVtbl->GetDesc(self, pDesc); + #endif +} + +static inline void _sgext_d3d11_CopySubresourceRegion(ID3D11DeviceContext* self, ID3D11Resource *pDstResource, UINT DstSubresource, UINT DstX, UINT DstY, UINT DstZ, ID3D11Resource *pSrcResource, UINT SrcSubresource, const D3D11_BOX *pSrcBox) { + #if defined(__cplusplus) + self->CopySubresourceRegion(pDstResource, DstSubresource, DstX, DstY, DstZ, pSrcResource, SrcSubresource, pSrcBox); + #else + self->lpVtbl->CopySubresourceRegion(self, pDstResource, DstSubresource, DstX, DstY, DstZ, pSrcResource, SrcSubresource, pSrcBox); + #endif +} + +static inline void _sgext_d3d11_OMGetRenderTargets(ID3D11DeviceContext* self, UINT NumViews, ID3D11RenderTargetView **ppRenderTargetViews, ID3D11DepthStencilView **ppDepthStencilView) { + #if defined(__cplusplus) + self->OMGetRenderTargets(NumViews, ppRenderTargetViews, ppDepthStencilView); + #else + self->lpVtbl->OMGetRenderTargets(self, NumViews, ppRenderTargetViews, ppDepthStencilView); + #endif +} + +static inline void _sgext_d3d11_RenderTargetView_GetResource(ID3D11RenderTargetView* self, ID3D11Resource** ppResource) { + #if defined(__cplusplus) + self->GetResource(ppResource); + #else + self->lpVtbl->GetResource(self, ppResource); + #endif +} + +static void _sg_d3d11_query_image_pixels(_sg_image_t* img, void* pixels) { + SOKOL_ASSERT(_sg.d3d11.ctx); + SOKOL_ASSERT(img->d3d11.tex2d); + HRESULT hr; + _SOKOL_UNUSED(hr); + + // create staging texture + ID3D11Texture2D* staging_tex = NULL; + D3D11_TEXTURE2D_DESC staging_desc = { + .Width = (UINT)img->cmn.width, + .Height = (UINT)img->cmn.height, + .MipLevels = 1, + .ArraySize = 1, + .Format = img->d3d11.format, + .SampleDesc = { + .Count = 1, + .Quality = 0, + }, + .Usage = D3D11_USAGE_STAGING, + .BindFlags = 0, + .CPUAccessFlags = D3D11_CPU_ACCESS_READ, + .MiscFlags = 0 + }; + hr = _sg_d3d11_CreateTexture2D(_sg.d3d11.dev, &staging_desc, NULL, &staging_tex); + SOKOL_ASSERT(SUCCEEDED(hr)); + + // copy pixels to staging texture + _sgext_d3d11_CopySubresourceRegion(_sg.d3d11.ctx, + (ID3D11Resource*)staging_tex, + 0, 0, 0, 0, + (ID3D11Resource*)img->d3d11.tex2d, + 0, NULL); + + // map the staging texture's data to CPU-accessible memory + D3D11_MAPPED_SUBRESOURCE msr = {.pData = NULL}; + hr = _sg_d3d11_Map(_sg.d3d11.ctx, (ID3D11Resource*)staging_tex, 0, D3D11_MAP_READ, 0, &msr); + SOKOL_ASSERT(SUCCEEDED(hr)); + + // copy the data into the desired buffer, converting pixels to the desired format at the same time + /* int res = SDL_ConvertPixels( + img->cmn.width, img->cmn.height, + _sg_d3d11_dxgi_format_to_sdl_pixel_format(staging_desc.Format), + msr.pData, msr.RowPitch, + SDL_PIXELFORMAT_RGBA32, + pixels, img->cmn.width * 4); + SOKOL_ASSERT(res == 0); + _SOKOL_UNUSED(res); + */ + // unmap the texture + _sg_d3d11_Unmap(_sg.d3d11.ctx, (ID3D11Resource*)staging_tex, 0); + + if(staging_tex) _sg_d3d11_Release(staging_tex); +} + +static void _sg_d3d11_query_pixels(int x, int y, int w, int h, bool origin_top_left, void *pixels) { + // get current render target + ID3D11RenderTargetView* render_target_view = NULL; + _sgext_d3d11_OMGetRenderTargets(_sg.d3d11.ctx, 1, &render_target_view, NULL); + + // fallback to window render target + if(!render_target_view) + render_target_view = (ID3D11RenderTargetView*)_sg.d3d11.rtv_cb(); + SOKOL_ASSERT(render_target_view); + + // get the back buffer texture + ID3D11Texture2D *back_buffer = NULL; + _sgext_d3d11_RenderTargetView_GetResource(render_target_view, (ID3D11Resource**)&back_buffer); + SOKOL_ASSERT(back_buffer); + + // create a staging texture to copy the screen's data to + D3D11_TEXTURE2D_DESC staging_desc; + _sgext_d3d11_Texture2D_GetDesc(back_buffer, &staging_desc); + staging_desc.Width = w; + staging_desc.Height = h; + staging_desc.BindFlags = 0; + staging_desc.MiscFlags = 0; + staging_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + staging_desc.Usage = D3D11_USAGE_STAGING; + ID3D11Texture2D *staging_tex = NULL; + HRESULT hr = _sg_d3d11_CreateTexture2D(_sg.d3d11.dev, &staging_desc, NULL, &staging_tex); + SOKOL_ASSERT(SUCCEEDED(hr)); + _SOKOL_UNUSED(hr); + + // copy the desired portion of the back buffer to the staging texture + y = (origin_top_left ? y : (_sg.d3d11.cur_height - (y + h))); + D3D11_BOX src_box = { + .left = (UINT)x, + .top = (UINT)y, + .front = 0, + .right = (UINT)(x + w), + .bottom = (UINT)(y + w), + .back = 1, + }; + _sgext_d3d11_CopySubresourceRegion(_sg.d3d11.ctx, + (ID3D11Resource*)staging_tex, + 0, 0, 0, 0, + (ID3D11Resource*)back_buffer, + 0, &src_box); + + // map the staging texture's data to CPU-accessible memory + D3D11_MAPPED_SUBRESOURCE msr = {.pData = NULL}; + hr = _sg_d3d11_Map(_sg.d3d11.ctx, (ID3D11Resource*)staging_tex, 0, D3D11_MAP_READ, 0, &msr); + SOKOL_ASSERT(SUCCEEDED(hr)); + + // copy the data into the desired buffer, converting pixels to the desired format at the same time + /* int res = SDL_ConvertPixels( + w, h, + _sg_d3d11_dxgi_format_to_sdl_pixel_format(staging_desc.Format), + msr.pData, msr.RowPitch, + SDL_PIXELFORMAT_RGBA32, + pixels, w * 4); + SOKOL_ASSERT(res == 0); + _SOKOL_UNUSED(res); + */ + // unmap the texture + _sg_d3d11_Unmap(_sg.d3d11.ctx, (ID3D11Resource*)staging_tex, 0); + + if(back_buffer) _sg_d3d11_Release(back_buffer); + if(staging_tex) _sg_d3d11_Release(staging_tex); +} + +static void _sg_d3d11_update_texture_filter(_sg_image_t* img, sg_filter min_filter, sg_filter mag_filter) { + SOKOL_ASSERT(img->d3d11.tex2d || img->d3d11.tex3d); + HRESULT hr; + _SOKOL_UNUSED(hr); + D3D11_SAMPLER_DESC d3d11_smp_desc; + memset(&d3d11_smp_desc, 0, sizeof(d3d11_smp_desc)); + _sgext_d3d11_SamplerState_GetDesc(img->d3d11.smp, &d3d11_smp_desc); + _sg_d3d11_Release(img->d3d11.smp); + img->cmn.min_filter = min_filter; + img->cmn.mag_filter = mag_filter; + d3d11_smp_desc.Filter = _sg_d3d11_filter(img->cmn.min_filter, img->cmn.mag_filter, img->cmn.max_anisotropy); + hr = _sg_d3d11_CreateSamplerState(_sg.d3d11.dev, &d3d11_smp_desc, &img->d3d11.smp); + SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11.smp); +} + +#elif defined(SOKOL_METAL) + +#import +#import + + +static void _sg_metal_commit_command_buffer() { + SOKOL_ASSERT(!_sg.mtl.in_pass); + if(_sg.mtl.cmd_buffer) { + #if defined(_SG_TARGET_MACOS) + [_sg.mtl.uniform_buffers[_sg.mtl.cur_frame_rotate_index] didModifyRange:NSMakeRange(0, _sg.mtl.cur_ub_offset)]; + #endif + [_sg.mtl.cmd_buffer commit]; + [_sg.mtl.cmd_buffer waitUntilCompleted]; + _sg.mtl.cmd_buffer = [_sg.mtl.cmd_queue commandBufferWithUnretainedReferences]; + } +} + +static void _sg_metal_encode_texture_pixels(int x, int y, int w, int h, bool origin_top_left, id mtl_src_texture, void* pixels) { + SOKOL_ASSERT(!_sg.mtl.in_pass); + _sg_metal_commit_command_buffer(); + MTLTextureDescriptor* mtl_dst_texture_desc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:mtl_src_texture.pixelFormat width:w height:h mipmapped:NO]; + mtl_dst_texture_desc.storageMode = MTLStorageModeManaged; + mtl_dst_texture_desc.resourceOptions = MTLResourceStorageModeManaged; + mtl_dst_texture_desc.usage = MTLTextureUsageShaderRead + MTLTextureUsageShaderWrite; + id mtl_dst_texture = [mtl_src_texture.device newTextureWithDescriptor:mtl_dst_texture_desc]; + id cmd_buffer = [_sg.mtl.cmd_queue commandBuffer]; + id blit_encoder = [cmd_buffer blitCommandEncoder]; + [blit_encoder copyFromTexture:mtl_src_texture + sourceSlice:0 + sourceLevel:0 + sourceOrigin:MTLOriginMake(x,(origin_top_left ? y : (mtl_src_texture.height - (y + h))),0) + sourceSize:MTLSizeMake(w,h,1) + toTexture:mtl_dst_texture + destinationSlice:0 + destinationLevel:0 + destinationOrigin:MTLOriginMake(0,0,0) + ]; + [blit_encoder synchronizeTexture:mtl_dst_texture slice:0 level:0]; + [blit_encoder endEncoding]; + [cmd_buffer commit]; + [cmd_buffer waitUntilCompleted]; + + MTLRegion mtl_region = MTLRegionMake2D(0, 0, w, h); + void* temp_pixels = (void*)SOKOL_MALLOC(w * 4 * h); + SOKOL_ASSERT(temp_pixels); + [mtl_dst_texture getBytes:temp_pixels bytesPerRow:w * 4 fromRegion:mtl_region mipmapLevel:0]; + // int res = SDL_ConvertPixels(w, h, _sg_metal_texture_format_to_sdl_pixel_format(mtl_dst_texture_desc.pixelFormat), temp_pixels, w * 4, SDL_PIXELFORMAT_RGBA32, pixels, w * 4); + SOKOL_FREE(temp_pixels); + SOKOL_ASSERT(res == 0); + _SOKOL_UNUSED(res); +} + +static void _sg_metal_query_image_pixels(_sg_image_t* img, void* pixels) { + id mtl_src_texture = _sg.mtl.idpool.pool[img->mtl.tex[0]]; + _sg_metal_encode_texture_pixels(0, 0, mtl_src_texture.width, mtl_src_texture.height, true, mtl_src_texture, pixels); +} + +static void _sg_metal_query_pixels(int x, int y, int w, int h, bool origin_top_left, void *pixels) { + id mtl_drawable = (__bridge id)_sg.mtl.drawable_cb(); + _sg_metal_encode_texture_pixels(x, y, w, h, origin_top_left, mtl_drawable.texture, pixels); +} + +static void _sg_metal_update_texture_filter(_sg_image_t* img, sg_filter min_filter, sg_filter mag_filter) { + sg_image_desc image_desc = { + .min_filter = min_filter, + .mag_filter = mag_filter, + .wrap_u = img->cmn.wrap_u, + .wrap_v = img->cmn.wrap_v, + .wrap_w = img->cmn.wrap_w, + .max_anisotropy = img->cmn.max_anisotropy, + .border_color = img->cmn.border_color, + }; + sg_image_desc desc_def = _sg_image_desc_defaults(&image_desc); + img->mtl.sampler_state = _sg_mtl_create_sampler(_sg.mtl.device, &desc_def); + img->cmn.min_filter = min_filter; + img->cmn.mag_filter = mag_filter; +} + +#endif + +void sg_query_image_pixels(sg_image img_id, void* pixels, int size) { + SOKOL_ASSERT(pixels); + SOKOL_ASSERT(img_id.id != SG_INVALID_ID); + _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + SOKOL_ASSERT(img); + SOKOL_ASSERT(size >= (img->cmn.width * img->cmn.height * 4)); + _SOKOL_UNUSED(size); +#if defined(_SOKOL_ANY_GL) + _sg_gl_query_image_pixels(img, pixels); +#elif defined(SOKOL_D3D11) + _sg_d3d11_query_image_pixels(img, pixels); +#elif defined(SOKOL_METAL) + _sg_metal_query_image_pixels(img, pixels); +#endif +} + +void sg_query_pixels(int x, int y, int w, int h, bool origin_top_left, void *pixels, int size) { + SOKOL_ASSERT(pixels); + SOKOL_ASSERT(size >= w*h); + _SOKOL_UNUSED(size); +#if defined(_SOKOL_ANY_GL) + _sg_gl_query_pixels(x, y, w, h, origin_top_left, pixels); +#elif defined(SOKOL_D3D11) + _sg_d3d11_query_pixels(x, y, w, h, origin_top_left, pixels); +#elif defined(SOKOL_METAL) + _sg_metal_query_pixels(x, y, w, h, origin_top_left, pixels); +#endif +} + +void sg_update_texture_filter(sg_image img_id, sg_filter min_filter, sg_filter mag_filter) { + SOKOL_ASSERT(img_id.id != SG_INVALID_ID); + _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + SOKOL_ASSERT(img); +#if defined(_SOKOL_ANY_GL) + _sg_gl_update_texture_filter(img, min_filter, mag_filter); +#elif defined(SOKOL_D3D11) + _sg_d3d11_update_texture_filter(img, min_filter, mag_filter); +#elif defined(SOKOL_METAL) + _sg_metal_update_texture_filter(img, min_filter, mag_filter); +#endif +} + +#endif // SOKOL_GFX_EXT_IMPL_INCLUDED +#endif // SOKOL_GFX_EXT_IMPL diff --git a/source/engine/yugine.c b/source/engine/yugine.c index 32acf4c..769c568 100644 --- a/source/engine/yugine.c +++ b/source/engine/yugine.c @@ -56,6 +56,7 @@ #include "stb_image.h" #define STB_IMAGE_WRITE_IMPLEMENTATION +#define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_BOX #include "stb_image_write.h" #define PL_MPEG_IMPLEMENTATION diff --git a/source/scripts/debug.js b/source/scripts/debug.js index a5ead3e..98366c2 100644 --- a/source/scripts/debug.js +++ b/source/scripts/debug.js @@ -100,6 +100,9 @@ var Debug = { GUI.text(x.fullpath(), world2screen(x.pos).add([0,32]), 1, [84,110,255]); }); + if (Debug.Options.gif.rec) + gui_text("REC", [0,40], 1); + gui_text(Game.playing() ? "PLAYING" : Game.stepping() ? "STEP" : @@ -241,8 +244,25 @@ DebugControls.inputs.f4 = function() { Debug.draw_gizmos = !Debug.draw_gizmos; }; DebugControls.inputs.f4.doc = "Toggle drawing gizmos and names of objects."; -DebugControls.inputs.f8 = function() { cmd(131); }; -DebugControls.inputs.f9 = function() { cmd(132, "out.gif"); }; + +Debug.Options.gif = { + w: 480, + h: 320, + cpf: 4, + depth: 8, + file: "out.gif", + rec: false, +}; +DebugControls.inputs.f8 = function() { + var gif = Debug.Options.gif; + cmd(131, gif.w, gif.h, gif.cpf, gif.depth); + gif.rec = true; +}; +DebugControls.inputs.f9 = function() { + cmd(132, Debug.Options.gif.file); + Debug.Options.gif.rec = false; +}; + DebugControls.inputs.f10 = function() { Time.timescale = 0.1; }; DebugControls.inputs.f10.doc = "Toggle timescale to 1/10."; DebugControls.inputs.f10.released = function () { Time.timescale = 1.0; };