406 lines
11 KiB
C
406 lines
11 KiB
C
/************************************************************/ /**
|
|
*
|
|
* @file: graphics_surface.c
|
|
* @author: Martin Fouilleul
|
|
* @date: 25/04/2023
|
|
*
|
|
*****************************************************************/
|
|
|
|
#include "graphics_surface.h"
|
|
|
|
//---------------------------------------------------------------
|
|
// per-thread selected surface
|
|
//---------------------------------------------------------------
|
|
|
|
oc_thread_local oc_surface oc_selectedSurface = { 0 };
|
|
|
|
//---------------------------------------------------------------
|
|
// typed handles functions
|
|
//---------------------------------------------------------------
|
|
|
|
oc_surface oc_surface_handle_alloc(oc_surface_data* surface)
|
|
{
|
|
oc_surface handle = { .h = oc_graphics_handle_alloc(OC_GRAPHICS_HANDLE_SURFACE, (void*)surface) };
|
|
return (handle);
|
|
}
|
|
|
|
oc_surface_data* oc_surface_data_from_handle(oc_surface handle)
|
|
{
|
|
oc_surface_data* data = oc_graphics_data_from_handle(OC_GRAPHICS_HANDLE_SURFACE, handle.h);
|
|
return (data);
|
|
}
|
|
|
|
oc_image oc_image_handle_alloc(oc_image_data* image)
|
|
{
|
|
oc_image handle = { .h = oc_graphics_handle_alloc(OC_GRAPHICS_HANDLE_IMAGE, (void*)image) };
|
|
return (handle);
|
|
}
|
|
|
|
oc_image_data* oc_image_data_from_handle(oc_image handle)
|
|
{
|
|
oc_image_data* data = oc_graphics_data_from_handle(OC_GRAPHICS_HANDLE_IMAGE, handle.h);
|
|
return (data);
|
|
}
|
|
|
|
//---------------------------------------------------------------
|
|
// surface API
|
|
//---------------------------------------------------------------
|
|
|
|
#if OC_COMPILE_GL
|
|
#if OC_PLATFORM_WINDOWS
|
|
#include "wgl_surface.h"
|
|
#define oc_gl_surface_create_for_window oc_wgl_surface_create_for_window
|
|
#endif
|
|
#endif
|
|
|
|
#if OC_COMPILE_GLES
|
|
#include "egl_surface.h"
|
|
#endif
|
|
|
|
#if OC_COMPILE_METAL
|
|
#include "mtl_surface.h"
|
|
#endif
|
|
|
|
#if OC_COMPILE_CANVAS
|
|
#if OC_PLATFORM_MACOS
|
|
oc_surface_data* oc_mtl_canvas_surface_create_for_window(oc_window window);
|
|
#elif OC_PLATFORM_WINDOWS
|
|
oc_surface_data* oc_gl_canvas_surface_create_for_window(oc_window window);
|
|
#endif
|
|
#endif
|
|
|
|
bool oc_is_surface_backend_available(oc_surface_api api)
|
|
{
|
|
bool result = false;
|
|
switch(api)
|
|
{
|
|
#if OC_COMPILE_METAL
|
|
case OC_METAL:
|
|
#endif
|
|
|
|
#if OC_COMPILE_GL
|
|
case OC_GL:
|
|
#endif
|
|
|
|
#if OC_COMPILE_GLES
|
|
case OC_GLES:
|
|
#endif
|
|
|
|
#if OC_COMPILE_CANVAS
|
|
case OC_CANVAS:
|
|
#endif
|
|
result = true;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return (result);
|
|
}
|
|
|
|
oc_surface oc_surface_nil() { return ((oc_surface){ .h = 0 }); }
|
|
|
|
bool oc_surface_is_nil(oc_surface surface) { return (surface.h == 0); }
|
|
|
|
oc_surface oc_surface_create_for_window(oc_window window, oc_surface_api api)
|
|
{
|
|
if(oc_graphicsData.init)
|
|
{
|
|
oc_graphics_init();
|
|
}
|
|
oc_surface surfaceHandle = oc_surface_nil();
|
|
oc_surface_data* surface = 0;
|
|
|
|
switch(api)
|
|
{
|
|
#if OC_COMPILE_GL
|
|
case OC_GL:
|
|
surface = oc_gl_surface_create_for_window(window);
|
|
break;
|
|
#endif
|
|
|
|
#if OC_COMPILE_GLES
|
|
case OC_GLES:
|
|
surface = oc_egl_surface_create_for_window(window);
|
|
break;
|
|
#endif
|
|
|
|
#if OC_COMPILE_METAL
|
|
case OC_METAL:
|
|
surface = oc_mtl_surface_create_for_window(window);
|
|
break;
|
|
#endif
|
|
|
|
#if OC_COMPILE_CANVAS
|
|
case OC_CANVAS:
|
|
|
|
#if OC_PLATFORM_MACOS
|
|
surface = oc_mtl_canvas_surface_create_for_window(window);
|
|
#elif OC_PLATFORM_WINDOWS
|
|
surface = oc_gl_canvas_surface_create_for_window(window);
|
|
#endif
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
break;
|
|
}
|
|
if(surface)
|
|
{
|
|
surfaceHandle = oc_surface_handle_alloc(surface);
|
|
oc_surface_swap_interval(surfaceHandle, 0);
|
|
oc_surface_select(surfaceHandle);
|
|
}
|
|
return (surfaceHandle);
|
|
}
|
|
|
|
void oc_surface_destroy(oc_surface handle)
|
|
{
|
|
OC_DEBUG_ASSERT(oc_graphicsData.init);
|
|
oc_surface_data* surface = oc_surface_data_from_handle(handle);
|
|
if(surface)
|
|
{
|
|
if(oc_selectedSurface.h == handle.h)
|
|
{
|
|
oc_surface_deselect();
|
|
}
|
|
|
|
if(surface->backend && surface->backend->destroy)
|
|
{
|
|
surface->backend->destroy(surface->backend);
|
|
}
|
|
surface->destroy(surface);
|
|
oc_graphics_handle_recycle(handle.h);
|
|
}
|
|
}
|
|
|
|
void oc_surface_deselect()
|
|
{
|
|
OC_DEBUG_ASSERT(oc_graphicsData.init);
|
|
|
|
oc_surface_data* prevSurface = oc_surface_data_from_handle(oc_selectedSurface);
|
|
if(prevSurface && prevSurface->deselect)
|
|
{
|
|
prevSurface->deselect(prevSurface);
|
|
}
|
|
oc_selectedSurface = oc_surface_nil();
|
|
}
|
|
|
|
void oc_surface_select(oc_surface surface)
|
|
{
|
|
OC_DEBUG_ASSERT(oc_graphicsData.init);
|
|
|
|
if(surface.h != oc_selectedSurface.h)
|
|
{
|
|
oc_surface_deselect();
|
|
}
|
|
|
|
oc_surface_data* surfaceData = oc_surface_data_from_handle(surface);
|
|
if(surfaceData && surfaceData->prepare)
|
|
{
|
|
surfaceData->prepare(surfaceData);
|
|
oc_selectedSurface = surface;
|
|
}
|
|
}
|
|
|
|
void oc_surface_present(oc_surface surface)
|
|
{
|
|
OC_DEBUG_ASSERT(oc_graphicsData.init);
|
|
oc_surface_data* surfaceData = oc_surface_data_from_handle(surface);
|
|
if(surfaceData && surfaceData->present)
|
|
{
|
|
surfaceData->present(surfaceData);
|
|
}
|
|
}
|
|
|
|
void oc_surface_swap_interval(oc_surface surface, int swap)
|
|
{
|
|
OC_DEBUG_ASSERT(oc_graphicsData.init);
|
|
oc_surface_data* surfaceData = oc_surface_data_from_handle(surface);
|
|
if(surfaceData && surfaceData->swapInterval)
|
|
{
|
|
surfaceData->swapInterval(surfaceData, swap);
|
|
}
|
|
}
|
|
|
|
oc_vec2 oc_surface_get_size(oc_surface surface)
|
|
{
|
|
OC_DEBUG_ASSERT(oc_graphicsData.init);
|
|
oc_vec2 size = { 0 };
|
|
oc_surface_data* surfaceData = oc_surface_data_from_handle(surface);
|
|
if(surfaceData && surfaceData->getSize)
|
|
{
|
|
size = surfaceData->getSize(surfaceData);
|
|
}
|
|
return (size);
|
|
}
|
|
|
|
oc_vec2 oc_surface_contents_scaling(oc_surface surface)
|
|
{
|
|
OC_DEBUG_ASSERT(oc_graphicsData.init);
|
|
oc_vec2 scaling = { 1, 1 };
|
|
oc_surface_data* surfaceData = oc_surface_data_from_handle(surface);
|
|
if(surfaceData && surfaceData->contentsScaling)
|
|
{
|
|
scaling = surfaceData->contentsScaling(surfaceData);
|
|
}
|
|
return (scaling);
|
|
}
|
|
|
|
void oc_surface_set_hidden(oc_surface surface, bool hidden)
|
|
{
|
|
OC_DEBUG_ASSERT(oc_graphicsData.init);
|
|
oc_surface_data* surfaceData = oc_surface_data_from_handle(surface);
|
|
if(surfaceData && surfaceData->setHidden)
|
|
{
|
|
surfaceData->setHidden(surfaceData, hidden);
|
|
}
|
|
}
|
|
|
|
bool oc_surface_get_hidden(oc_surface surface)
|
|
{
|
|
OC_DEBUG_ASSERT(oc_graphicsData.init);
|
|
bool res = false;
|
|
oc_surface_data* surfaceData = oc_surface_data_from_handle(surface);
|
|
if(surfaceData && surfaceData->getHidden)
|
|
{
|
|
res = surfaceData->getHidden(surfaceData);
|
|
}
|
|
return (res);
|
|
}
|
|
|
|
void* oc_surface_native_layer(oc_surface surface)
|
|
{
|
|
void* res = 0;
|
|
oc_surface_data* surfaceData = oc_surface_data_from_handle(surface);
|
|
if(surfaceData && surfaceData->nativeLayer)
|
|
{
|
|
res = surfaceData->nativeLayer(surfaceData);
|
|
}
|
|
return (res);
|
|
}
|
|
|
|
void oc_surface_render_commands(oc_surface surface,
|
|
oc_color clearColor,
|
|
u32 primitiveCount,
|
|
oc_primitive* primitives,
|
|
u32 eltCount,
|
|
oc_path_elt* elements)
|
|
{
|
|
oc_surface_data* surfaceData = oc_surface_data_from_handle(surface);
|
|
|
|
if(surface.h != oc_selectedSurface.h)
|
|
{
|
|
oc_log_error("surface is not selected. Make sure to call oc_surface_select() before drawing onto a surface.\n");
|
|
}
|
|
else if(surfaceData && surfaceData->backend)
|
|
{
|
|
surfaceData->backend->render(surfaceData->backend,
|
|
clearColor,
|
|
primitiveCount,
|
|
primitives,
|
|
eltCount,
|
|
elements);
|
|
}
|
|
}
|
|
|
|
void oc_surface_bring_to_front(oc_surface handle)
|
|
{
|
|
oc_surface_data* surface = oc_surface_data_from_handle(handle);
|
|
if(surface && surface->bringToFront)
|
|
{
|
|
surface->bringToFront(surface);
|
|
}
|
|
}
|
|
|
|
void oc_surface_send_to_back(oc_surface handle)
|
|
{
|
|
oc_surface_data* surface = oc_surface_data_from_handle(handle);
|
|
if(surface && surface->sendToBack)
|
|
{
|
|
surface->sendToBack(surface);
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------
|
|
//NOTE(martin): images
|
|
//------------------------------------------------------------------------------------------
|
|
|
|
oc_vec2 oc_image_size(oc_image image)
|
|
{
|
|
oc_vec2 res = { 0 };
|
|
oc_image_data* imageData = oc_image_data_from_handle(image);
|
|
if(imageData)
|
|
{
|
|
res = imageData->size;
|
|
}
|
|
return (res);
|
|
}
|
|
|
|
oc_image oc_image_create(oc_surface surface, u32 width, u32 height)
|
|
{
|
|
oc_image image = oc_image_nil();
|
|
oc_surface_data* surfaceData = oc_surface_data_from_handle(surface);
|
|
|
|
if(surface.h != oc_selectedSurface.h)
|
|
{
|
|
oc_log_error("surface is not selected. Make sure to call oc_surface_select() before modifying graphics resources.\n");
|
|
}
|
|
else if(surfaceData && surfaceData->backend)
|
|
{
|
|
OC_DEBUG_ASSERT(surfaceData->api == OC_CANVAS);
|
|
|
|
oc_image_data* imageData = surfaceData->backend->imageCreate(surfaceData->backend, (oc_vec2){ width, height });
|
|
if(imageData)
|
|
{
|
|
imageData->surface = surface;
|
|
image = oc_image_handle_alloc(imageData);
|
|
}
|
|
}
|
|
return (image);
|
|
}
|
|
|
|
void oc_image_destroy(oc_image image)
|
|
{
|
|
oc_image_data* imageData = oc_image_data_from_handle(image);
|
|
|
|
if(imageData)
|
|
{
|
|
if(imageData->surface.h != oc_selectedSurface.h)
|
|
{
|
|
oc_log_error("surface is not selected. Make sure to call oc_surface_select() before modifying graphics resources.\n");
|
|
}
|
|
else
|
|
{
|
|
oc_surface_data* surface = oc_surface_data_from_handle(imageData->surface);
|
|
if(surface && surface->backend)
|
|
{
|
|
surface->backend->imageDestroy(surface->backend, imageData);
|
|
oc_graphics_handle_recycle(image.h);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void oc_image_upload_region_rgba8(oc_image image, oc_rect region, u8* pixels)
|
|
{
|
|
oc_image_data* imageData = oc_image_data_from_handle(image);
|
|
|
|
if(imageData)
|
|
{
|
|
if(imageData->surface.h != oc_selectedSurface.h)
|
|
{
|
|
oc_log_error("surface is not selected. Make sure to call oc_surface_select() before modifying graphics resources.\n");
|
|
}
|
|
else
|
|
{
|
|
oc_surface_data* surfaceData = oc_surface_data_from_handle(imageData->surface);
|
|
if(surfaceData)
|
|
{
|
|
OC_DEBUG_ASSERT(surfaceData->backend);
|
|
surfaceData->backend->imageUploadRegion(surfaceData->backend, imageData, region, pixels);
|
|
}
|
|
}
|
|
}
|
|
}
|