2016-11-19 09:28:01 +00:00
# include "graphics/graphics.h"
2022-05-09 18:47:06 +00:00
# include "data/blob.h"
2022-04-30 03:38:34 +00:00
# include "data/image.h"
2022-05-11 19:51:13 +00:00
# include "math/math.h"
2022-04-21 07:27:13 +00:00
# include "core/gpu.h"
2022-05-07 00:26:38 +00:00
# include "core/maf.h"
2022-04-27 05:51:24 +00:00
# include "core/os.h"
2022-04-21 07:27:13 +00:00
# include "util.h"
2022-04-30 03:38:34 +00:00
# include <math.h>
2022-04-26 22:32:54 +00:00
# include <stdlib.h>
2022-04-20 07:38:21 +00:00
# include <string.h>
2022-05-09 18:47:06 +00:00
# ifdef LOVR_USE_GLSLANG
# include "glslang_c_interface.h"
# include "resource_limits_c.h"
# endif
2019-06-28 05:17:50 +00:00
2022-05-11 22:28:04 +00:00
uint32_t os_vk_create_surface ( void * instance , void * * surface ) ;
const char * * os_vk_get_instance_extensions ( uint32_t * count ) ;
2022-04-27 05:44:44 +00:00
# define MAX_FRAME_MEMORY (1 << 30)
2022-04-26 22:32:54 +00:00
struct Buffer {
uint32_t ref ;
uint32_t size ;
gpu_buffer * gpu ;
BufferInfo info ;
2022-04-27 07:35:09 +00:00
char * pointer ;
2022-04-26 22:32:54 +00:00
} ;
2022-04-30 03:38:34 +00:00
struct Texture {
uint32_t ref ;
gpu_texture * gpu ;
gpu_texture * renderView ;
TextureInfo info ;
} ;
2022-05-01 22:47:17 +00:00
struct Sampler {
uint32_t ref ;
gpu_sampler * gpu ;
SamplerInfo info ;
} ;
2022-05-09 18:47:06 +00:00
struct Shader {
uint32_t ref ;
gpu_shader * gpu ;
ShaderInfo info ;
} ;
2022-05-11 19:50:26 +00:00
typedef struct {
float color [ 4 ] ;
Shader * shader ;
gpu_pipeline_info info ;
bool dirty ;
} Pipeline ;
2022-04-29 05:30:31 +00:00
struct Pass {
uint32_t ref ;
PassInfo info ;
gpu_stream * stream ;
2022-05-07 00:26:38 +00:00
float * transform ;
float transforms [ 16 ] [ 16 ] ;
2022-05-11 19:50:26 +00:00
uint32_t transformIndex ;
Pipeline * pipeline ;
Pipeline pipelines [ 4 ] ;
uint32_t pipelineIndex ;
2022-04-29 05:30:31 +00:00
} ;
2022-05-11 19:51:13 +00:00
typedef struct {
2022-05-11 22:28:04 +00:00
gpu_texture * texture ;
2022-05-11 19:51:13 +00:00
uint32_t hash ;
uint32_t tick ;
2022-05-11 22:28:04 +00:00
} Attachment ;
2022-05-11 19:51:13 +00:00
2022-04-27 05:44:44 +00:00
typedef struct {
char * memory ;
uint32_t cursor ;
uint32_t length ;
} Allocator ;
2019-06-27 08:47:08 +00:00
static struct {
bool initialized ;
2022-04-29 05:30:31 +00:00
bool active ;
uint32_t tick ;
Pass * transfers ;
2022-04-22 20:28:59 +00:00
gpu_device_info device ;
2022-04-21 09:16:17 +00:00
gpu_features features ;
gpu_limits limits ;
2022-05-11 22:28:04 +00:00
Texture * window ;
Attachment attachments [ 16 ] ;
2022-04-27 05:44:44 +00:00
Allocator allocator ;
2019-06-27 08:47:08 +00:00
} state ;
2016-07-07 07:04:24 +00:00
2022-04-21 07:27:13 +00:00
// Helpers
2022-04-29 05:30:31 +00:00
static void * tempAlloc ( size_t size ) ;
static void beginFrame ( void ) ;
static gpu_stream * getTransfers ( void ) ;
2022-05-11 22:28:04 +00:00
static gpu_texture * getAttachment ( uint32_t size [ 2 ] , uint32_t layers , TextureFormat format , bool srgb , uint32_t samples ) ;
2022-04-30 03:38:34 +00:00
static size_t measureTexture ( TextureFormat format , uint16_t w , uint16_t h , uint16_t d ) ;
2022-04-21 07:27:13 +00:00
static void onMessage ( void * context , const char * message , bool severe ) ;
// Entry
2022-05-11 22:28:04 +00:00
bool lovrGraphicsInit ( bool debug , bool vsync ) {
2022-04-19 02:30:58 +00:00
if ( state . initialized ) return false ;
2022-04-21 07:27:13 +00:00
2022-05-09 18:47:06 +00:00
glslang_initialize_process ( ) ;
2022-04-27 07:28:39 +00:00
float16Init ( ) ;
2022-04-21 07:27:13 +00:00
gpu_config config = {
. debug = debug ,
2022-04-21 09:16:17 +00:00
. callback = onMessage ,
2022-04-27 07:21:04 +00:00
. engineName = " LOVR " ,
. engineVersion = { LOVR_VERSION_MAJOR , LOVR_VERSION_MINOR , LOVR_VERSION_PATCH } ,
2022-04-21 09:16:17 +00:00
. device = & state . device ,
. features = & state . features ,
. limits = & state . limits
2022-04-21 07:27:13 +00:00
} ;
2022-05-11 22:28:04 +00:00
# ifdef LOVR_VK
if ( os_window_is_open ( ) ) {
config . vk . getInstanceExtensions = os_vk_get_instance_extensions ;
config . vk . createSurface = os_vk_create_surface ;
config . vk . surface = true ;
config . vk . vsync = vsync ;
}
# endif
2022-04-21 07:27:13 +00:00
if ( ! gpu_init ( & config ) ) {
lovrThrow ( " Failed to initialize GPU " ) ;
}
2022-04-27 05:44:44 +00:00
// Temporary frame memory uses a large 1GB virtual memory allocation, committing pages as needed
state . allocator . length = 1 < < 14 ;
state . allocator . memory = os_vm_init ( MAX_FRAME_MEMORY ) ;
os_vm_commit ( state . allocator . memory , state . allocator . length ) ;
2022-04-19 02:30:58 +00:00
state . initialized = true ;
return true ;
2016-09-28 03:20:08 +00:00
}
2016-10-04 22:13:57 +00:00
void lovrGraphicsDestroy ( ) {
2018-02-23 03:18:36 +00:00
if ( ! state . initialized ) return ;
2022-05-11 22:28:04 +00:00
for ( uint32_t i = 0 ; i < COUNTOF ( state . attachments ) ; i + + ) {
if ( state . attachments [ i ] . texture ) {
gpu_texture_destroy ( state . attachments [ i ] . texture ) ;
free ( state . attachments [ i ] . texture ) ;
2022-05-11 19:51:13 +00:00
}
}
2022-05-11 22:28:04 +00:00
lovrRelease ( state . window , lovrTextureDestroy ) ;
2022-04-21 07:27:13 +00:00
gpu_destroy ( ) ;
2022-05-09 18:47:06 +00:00
glslang_finalize_process ( ) ;
2022-04-27 05:44:44 +00:00
os_vm_free ( state . allocator . memory , MAX_FRAME_MEMORY ) ;
2019-06-27 08:47:08 +00:00
memset ( & state , 0 , sizeof ( state ) ) ;
2016-07-07 07:04:24 +00:00
}
2022-04-21 07:27:13 +00:00
2022-04-21 09:16:17 +00:00
void lovrGraphicsGetDevice ( GraphicsDevice * device ) {
2022-04-22 20:28:59 +00:00
device - > deviceId = state . device . deviceId ;
device - > vendorId = state . device . vendorId ;
device - > name = state . device . deviceName ;
2022-04-21 09:16:17 +00:00
device - > renderer = state . device . renderer ;
device - > subgroupSize = state . device . subgroupSize ;
2022-04-22 20:28:59 +00:00
device - > discrete = state . device . discrete ;
2022-04-21 09:16:17 +00:00
}
void lovrGraphicsGetFeatures ( GraphicsFeatures * features ) {
2022-04-26 22:31:51 +00:00
features - > textureBC = state . features . textureBC ;
features - > textureASTC = state . features . textureASTC ;
2022-04-21 09:16:17 +00:00
features - > wireframe = state . features . wireframe ;
features - > depthClamp = state . features . depthClamp ;
features - > indirectDrawFirstInstance = state . features . indirectDrawFirstInstance ;
features - > float64 = state . features . float64 ;
features - > int64 = state . features . int64 ;
features - > int16 = state . features . int16 ;
}
void lovrGraphicsGetLimits ( GraphicsLimits * limits ) {
limits - > textureSize2D = state . limits . textureSize2D ;
limits - > textureSize3D = state . limits . textureSize3D ;
limits - > textureSizeCube = state . limits . textureSizeCube ;
limits - > textureLayers = state . limits . textureLayers ;
limits - > renderSize [ 0 ] = state . limits . renderSize [ 0 ] ;
limits - > renderSize [ 1 ] = state . limits . renderSize [ 1 ] ;
limits - > renderSize [ 2 ] = state . limits . renderSize [ 2 ] ;
limits - > uniformBufferRange = state . limits . uniformBufferRange ;
limits - > storageBufferRange = state . limits . storageBufferRange ;
limits - > uniformBufferAlign = state . limits . uniformBufferAlign ;
limits - > storageBufferAlign = state . limits . storageBufferAlign ;
limits - > vertexAttributes = state . limits . vertexAttributes ;
limits - > vertexBufferStride = state . limits . vertexBufferStride ;
limits - > vertexShaderOutputs = state . limits . vertexShaderOutputs ;
2022-04-26 22:31:51 +00:00
limits - > clipDistances = state . limits . clipDistances ;
limits - > cullDistances = state . limits . cullDistances ;
limits - > clipAndCullDistances = state . limits . clipAndCullDistances ;
2022-04-21 09:16:17 +00:00
memcpy ( limits - > computeDispatchCount , state . limits . computeDispatchCount , 3 * sizeof ( uint32_t ) ) ;
memcpy ( limits - > computeWorkgroupSize , state . limits . computeWorkgroupSize , 3 * sizeof ( uint32_t ) ) ;
limits - > computeWorkgroupVolume = state . limits . computeWorkgroupVolume ;
limits - > computeSharedMemory = state . limits . computeSharedMemory ;
limits - > shaderConstantSize = state . limits . pushConstantSize ;
limits - > indirectDrawCount = state . limits . indirectDrawCount ;
limits - > instances = state . limits . instances ;
limits - > anisotropy = state . limits . anisotropy ;
limits - > pointSize = state . limits . pointSize ;
}
2022-04-30 00:12:10 +00:00
bool lovrGraphicsIsFormatSupported ( uint32_t format , uint32_t features ) {
uint8_t supports = state . features . formats [ format ] ;
if ( ! features ) return supports ;
if ( ( features & TEXTURE_FEATURE_SAMPLE ) & & ! ( supports & GPU_FEATURE_SAMPLE ) ) return false ;
if ( ( features & TEXTURE_FEATURE_FILTER ) & & ! ( supports & GPU_FEATURE_FILTER ) ) return false ;
if ( ( features & TEXTURE_FEATURE_RENDER ) & & ! ( supports & GPU_FEATURE_RENDER ) ) return false ;
if ( ( features & TEXTURE_FEATURE_BLEND ) & & ! ( supports & GPU_FEATURE_BLEND ) ) return false ;
if ( ( features & TEXTURE_FEATURE_STORAGE ) & & ! ( supports & GPU_FEATURE_STORAGE ) ) return false ;
if ( ( features & TEXTURE_FEATURE_ATOMIC ) & & ! ( supports & GPU_FEATURE_ATOMIC ) ) return false ;
if ( ( features & TEXTURE_FEATURE_BLIT_SRC ) & & ! ( supports & GPU_FEATURE_BLIT_SRC ) ) return false ;
if ( ( features & TEXTURE_FEATURE_BLIT_DST ) & & ! ( supports & GPU_FEATURE_BLIT_DST ) ) return false ;
return true ;
}
2022-04-29 05:30:31 +00:00
void lovrGraphicsSubmit ( Pass * * passes , uint32_t count ) {
if ( ! state . active ) {
return ;
}
2022-05-11 22:28:04 +00:00
if ( state . window ) {
state . window - > gpu = state . window - > renderView = NULL ;
}
2022-04-29 05:30:31 +00:00
// Allocate a few extra stream handles for any internal passes we sneak in
gpu_stream * * streams = tempAlloc ( ( count + 3 ) * sizeof ( gpu_stream * ) ) ;
uint32_t extraPassCount = 0 ;
if ( state . transfers ) {
streams [ extraPassCount + + ] = state . transfers - > stream ;
}
for ( uint32_t i = 0 ; i < count ; i + + ) {
streams [ extraPassCount + i ] = passes [ i ] - > stream ;
2022-05-11 19:51:13 +00:00
if ( passes [ i ] - > info . type = = PASS_RENDER ) {
gpu_render_end ( passes [ i ] - > stream ) ;
}
2022-04-29 05:30:31 +00:00
}
for ( uint32_t i = 0 ; i < extraPassCount + count ; i + + ) {
gpu_stream_end ( streams [ i ] ) ;
}
gpu_submit ( streams , extraPassCount + count ) ;
state . transfers = NULL ;
state . active = false ;
}
2022-04-29 05:37:03 +00:00
void lovrGraphicsWait ( ) {
gpu_wait ( ) ;
}
2022-04-26 22:32:54 +00:00
// Buffer
2022-04-27 07:28:39 +00:00
Buffer * lovrGraphicsGetBuffer ( BufferInfo * info , void * * data ) {
2022-04-28 22:39:45 +00:00
uint32_t size = info - > length * info - > stride ;
2022-04-27 07:28:39 +00:00
lovrCheck ( size > 0 , " Buffer size can not be zero " ) ;
2022-04-30 03:38:34 +00:00
lovrCheck ( size < = 1 < < 30 , " Max buffer size is 1GB " ) ;
2022-04-27 07:28:39 +00:00
2022-04-29 05:30:31 +00:00
Buffer * buffer = tempAlloc ( sizeof ( Buffer ) + gpu_sizeof_buffer ( ) ) ;
2022-04-27 07:28:39 +00:00
buffer - > ref = 1 ;
buffer - > size = size ;
buffer - > gpu = ( gpu_buffer * ) ( buffer + 1 ) ;
buffer - > info = * info ;
buffer - > pointer = gpu_map ( buffer - > gpu , size , state . limits . uniformBufferAlign , GPU_MAP_WRITE ) ;
if ( data ) {
* data = buffer - > pointer ;
}
return buffer ;
}
2022-04-26 22:32:54 +00:00
Buffer * lovrBufferCreate ( BufferInfo * info , void * * data ) {
2022-04-28 22:39:45 +00:00
uint32_t size = info - > length * info - > stride ;
2022-04-26 22:32:54 +00:00
lovrCheck ( size > 0 , " Buffer size can not be zero " ) ;
lovrCheck ( size < = 1 < < 30 , " Max buffer size is 1GB " ) ;
Buffer * buffer = calloc ( 1 , sizeof ( Buffer ) + gpu_sizeof_buffer ( ) ) ;
lovrAssert ( buffer , " Out of memory " ) ;
buffer - > ref = 1 ;
buffer - > size = size ;
buffer - > gpu = ( gpu_buffer * ) ( buffer + 1 ) ;
buffer - > info = * info ;
gpu_buffer_init ( buffer - > gpu , & ( gpu_buffer_info ) {
. size = buffer - > size ,
. label = info - > label ,
. pointer = data
} ) ;
2022-04-27 07:28:39 +00:00
if ( data & & * data = = NULL ) {
2022-04-29 05:30:31 +00:00
gpu_buffer * scratchpad = tempAlloc ( gpu_sizeof_buffer ( ) ) ;
2022-04-27 07:28:39 +00:00
* data = gpu_map ( scratchpad , size , 4 , GPU_MAP_WRITE ) ;
// TODO copy scratchpad to buffer
}
2022-04-26 22:32:54 +00:00
return buffer ;
}
void lovrBufferDestroy ( void * ref ) {
Buffer * buffer = ref ;
2022-04-27 07:28:39 +00:00
if ( buffer - > pointer ) return ;
2022-04-26 22:32:54 +00:00
gpu_buffer_destroy ( buffer - > gpu ) ;
free ( buffer ) ;
}
const BufferInfo * lovrBufferGetInfo ( Buffer * buffer ) {
return & buffer - > info ;
}
2022-04-27 07:28:39 +00:00
bool lovrBufferIsTemporary ( Buffer * buffer ) {
return ! ! buffer - > pointer ;
}
void * lovrBufferMap ( Buffer * buffer , uint32_t offset , uint32_t size ) {
if ( size = = ~ 0u ) {
size = buffer - > size - offset ;
}
lovrCheck ( offset + size < = buffer - > size , " Buffer write range [%d,%d] exceeds buffer size " , offset , offset + size ) ;
if ( buffer - > pointer ) {
2022-04-27 07:35:09 +00:00
return buffer - > pointer + offset ;
2022-04-27 07:28:39 +00:00
}
2022-04-29 05:30:31 +00:00
gpu_stream * transfers = getTransfers ( ) ;
gpu_buffer * scratchpad = tempAlloc ( gpu_sizeof_buffer ( ) ) ;
void * data = gpu_map ( scratchpad , size , 4 , GPU_MAP_WRITE ) ;
gpu_copy_buffers ( transfers , scratchpad , buffer - > gpu , 0 , offset , size ) ;
return data ;
2022-04-27 07:28:39 +00:00
}
void lovrBufferClear ( Buffer * buffer , uint32_t offset , uint32_t size ) {
2022-04-29 05:30:31 +00:00
lovrCheck ( size % 4 = = 0 , " Buffer clear size must be a multiple of 4 " ) ;
lovrCheck ( offset % 4 = = 0 , " Buffer clear offset must be a multiple of 4 " ) ;
2022-04-27 07:28:39 +00:00
lovrCheck ( offset + size < = buffer - > size , " Tried to clear past the end of the Buffer " ) ;
if ( buffer - > pointer ) {
2022-04-27 07:35:09 +00:00
memset ( buffer - > pointer + offset , 0 , size ) ;
2022-04-27 07:28:39 +00:00
} else {
2022-04-29 05:30:31 +00:00
gpu_stream * transfers = getTransfers ( ) ;
gpu_clear_buffer ( transfers , buffer - > gpu , offset , size ) ;
2022-04-27 07:28:39 +00:00
}
}
2022-04-30 03:38:34 +00:00
// Texture
2022-05-11 22:28:04 +00:00
Texture * lovrGraphicsGetWindowTexture ( ) {
if ( ! state . window ) {
state . window = malloc ( sizeof ( Texture ) ) ;
lovrAssert ( state . window , " Out of memory " ) ;
int width , height ;
os_window_get_fbsize ( & width , & height ) ;
state . window - > ref = 1 ;
state . window - > gpu = NULL ;
state . window - > renderView = NULL ;
state . window - > info = ( TextureInfo ) {
. type = TEXTURE_2D ,
. format = GPU_FORMAT_SURFACE ,
. width = width ,
. height = height ,
. depth = 1 ,
. mipmaps = 1 ,
. samples = 1 ,
. usage = TEXTURE_RENDER ,
. srgb = true
} ;
}
if ( ! state . window - > gpu ) {
beginFrame ( ) ;
state . window - > gpu = gpu_surface_acquire ( ) ;
state . window - > renderView = state . window - > gpu ;
}
return state . window ;
}
2022-04-30 03:38:34 +00:00
Texture * lovrTextureCreate ( TextureInfo * info ) {
uint32_t limits [ ] = {
[ TEXTURE_2D ] = state . limits . textureSize2D ,
2022-04-30 03:56:23 +00:00
[ TEXTURE_3D ] = state . limits . textureSize3D ,
2022-04-30 03:38:34 +00:00
[ TEXTURE_CUBE ] = state . limits . textureSizeCube ,
2022-04-30 03:56:23 +00:00
[ TEXTURE_ARRAY ] = state . limits . textureSize2D
2022-04-30 03:38:34 +00:00
} ;
uint32_t limit = limits [ info - > type ] ;
2022-05-01 01:49:46 +00:00
uint32_t mipmapCap = log2 ( MAX ( MAX ( info - > width , info - > height ) , ( info - > type = = TEXTURE_3D ? info - > depth : 1 ) ) ) + 1 ;
uint32_t mipmaps = CLAMP ( info - > mipmaps , 1 , mipmapCap ) ;
2022-04-30 03:38:34 +00:00
uint8_t supports = state . features . formats [ info - > format ] ;
lovrCheck ( info - > width > 0 , " Texture width must be greater than zero " ) ;
lovrCheck ( info - > height > 0 , " Texture height must be greater than zero " ) ;
lovrCheck ( info - > depth > 0 , " Texture depth must be greater than zero " ) ;
lovrCheck ( info - > width < = limit , " Texture %s exceeds the limit for this texture type (%d) " , " width " , limit ) ;
lovrCheck ( info - > height < = limit , " Texture %s exceeds the limit for this texture type (%d) " , " height " , limit ) ;
2022-04-30 03:56:23 +00:00
lovrCheck ( info - > depth < = limit | | info - > type ! = TEXTURE_3D , " Texture %s exceeds the limit for this texture type (%d) " , " depth " , limit ) ;
2022-04-30 03:38:34 +00:00
lovrCheck ( info - > depth < = state . limits . textureLayers | | info - > type ! = TEXTURE_ARRAY , " Texture %s exceeds the limit for this texture type (%d) " , " depth " , limit ) ;
lovrCheck ( info - > depth = = 1 | | info - > type ! = TEXTURE_2D , " 2D textures must have a depth of 1 " ) ;
lovrCheck ( info - > depth = = 6 | | info - > type ! = TEXTURE_CUBE , " Cubemaps must have a depth of 6 " ) ;
lovrCheck ( info - > width = = info - > height | | info - > type ! = TEXTURE_CUBE , " Cubemaps must be square " ) ;
2022-05-01 01:49:46 +00:00
lovrCheck ( measureTexture ( info - > format , info - > width , info - > height , info - > depth ) < 1 < < 30 , " Memory for a Texture can not exceed 1GB " ) ; // TODO mip?
2022-04-30 03:38:34 +00:00
lovrCheck ( info - > samples = = 1 | | info - > samples = = 4 , " Currently, Texture multisample count must be 1 or 4 " ) ;
lovrCheck ( info - > samples = = 1 | | info - > type ! = TEXTURE_CUBE , " Cubemaps can not be multisampled " ) ;
2022-04-30 03:56:23 +00:00
lovrCheck ( info - > samples = = 1 | | info - > type ! = TEXTURE_3D , " Volume textures can not be multisampled " ) ;
2022-04-30 03:38:34 +00:00
lovrCheck ( info - > samples = = 1 | | ~ info - > usage & TEXTURE_STORAGE , " Currently, Textures with the 'storage' flag can not be multisampled " ) ;
2022-05-01 01:49:46 +00:00
lovrCheck ( info - > samples = = 1 | | mipmaps = = 1 , " Multisampled textures can only have 1 mipmap " ) ;
2022-04-30 03:38:34 +00:00
lovrCheck ( ~ info - > usage & TEXTURE_SAMPLE | | ( supports & GPU_FEATURE_SAMPLE ) , " GPU does not support the 'sample' flag for this format " ) ;
lovrCheck ( ~ info - > usage & TEXTURE_RENDER | | ( supports & GPU_FEATURE_RENDER ) , " GPU does not support the 'render' flag for this format " ) ;
lovrCheck ( ~ info - > usage & TEXTURE_STORAGE | | ( supports & GPU_FEATURE_STORAGE ) , " GPU does not support the 'storage' flag for this format " ) ;
lovrCheck ( ~ info - > usage & TEXTURE_RENDER | | info - > width < = state . limits . renderSize [ 0 ] , " Texture has 'render' flag but its size exceeds the renderSize limit " ) ;
lovrCheck ( ~ info - > usage & TEXTURE_RENDER | | info - > height < = state . limits . renderSize [ 1 ] , " Texture has 'render' flag but its size exceeds the renderSize limit " ) ;
2022-05-01 01:49:46 +00:00
lovrCheck ( mipmaps < = mipmapCap , " Texture has more than the max number of mipmap levels for its size (%d) " , mipmapCap ) ;
2022-04-30 03:38:34 +00:00
lovrCheck ( ( info - > format < FORMAT_BC1 | | info - > format > FORMAT_BC7 ) | | state . features . textureBC , " %s textures are not supported on this GPU " , " BC " ) ;
lovrCheck ( info - > format < FORMAT_ASTC_4x4 | | state . features . textureASTC , " %s textures are not supported on this GPU " , " ASTC " ) ;
Texture * texture = calloc ( 1 , sizeof ( Texture ) + gpu_sizeof_texture ( ) ) ;
lovrAssert ( texture , " Out of memory " ) ;
texture - > ref = 1 ;
texture - > gpu = ( gpu_texture * ) ( texture + 1 ) ;
texture - > info = * info ;
2022-05-01 01:49:46 +00:00
texture - > info . mipmaps = mipmaps ;
uint32_t levelCount = 0 ;
uint32_t levelOffsets [ 16 ] ;
uint32_t levelSizes [ 16 ] ;
2022-05-01 22:18:56 +00:00
gpu_buffer * scratchpad = NULL ;
2022-05-01 01:49:46 +00:00
if ( info - > imageCount > 0 ) {
levelCount = lovrImageGetLevelCount ( info - > images [ 0 ] ) ;
lovrCheck ( info - > type ! = TEXTURE_3D | | levelCount = = 1 , " Images used to initialize 3D textures can not have mipmaps " ) ;
uint32_t total = 0 ;
for ( uint32_t level = 0 ; level < levelCount ; level + + ) {
levelOffsets [ level ] = total ;
uint32_t width = MAX ( info - > width > > level , 1 ) ;
uint32_t height = MAX ( info - > height > > level , 1 ) ;
levelSizes [ level ] = measureTexture ( info - > format , width , height , info - > depth ) ;
total + = levelSizes [ level ] ;
}
scratchpad = tempAlloc ( gpu_sizeof_buffer ( ) ) ;
char * data = gpu_map ( scratchpad , total , 64 , GPU_MAP_WRITE ) ;
for ( uint32_t level = 0 ; level < levelCount ; level + + ) {
for ( uint32_t layer = 0 ; layer < info - > depth ; layer + + ) {
Image * image = info - > imageCount = = 1 ? info - > images [ 0 ] : info - > images [ layer ] ;
uint32_t slice = info - > imageCount = = 1 ? layer : 0 ;
uint32_t size = lovrImageGetLayerSize ( image , level ) ;
lovrCheck ( size = = levelSizes [ level ] , " Texture/Image size mismatch! " ) ;
2022-05-01 22:18:56 +00:00
void * pixels = lovrImageGetLayerData ( image , level , slice ) ;
2022-05-01 01:49:46 +00:00
memcpy ( data , pixels , size ) ;
data + = size ;
}
}
}
2022-04-30 03:38:34 +00:00
gpu_texture_init ( texture - > gpu , & ( gpu_texture_info ) {
. type = ( gpu_texture_type ) info - > type ,
. format = ( gpu_texture_format ) info - > format ,
. size = { info - > width , info - > height , info - > depth } ,
. mipmaps = texture - > info . mipmaps ,
. samples = MAX ( info - > samples , 1 ) ,
. usage =
( ( info - > usage & TEXTURE_SAMPLE ) ? GPU_TEXTURE_SAMPLE : 0 ) |
( ( info - > usage & TEXTURE_RENDER ) ? GPU_TEXTURE_RENDER : 0 ) |
( ( info - > usage & TEXTURE_STORAGE ) ? GPU_TEXTURE_STORAGE : 0 ) |
2022-05-07 00:26:59 +00:00
( ( info - > usage & TEXTURE_TRANSFER ) ? GPU_TEXTURE_COPY_SRC | GPU_TEXTURE_COPY_DST : 0 ) ,
2022-04-30 03:38:34 +00:00
. srgb = info - > srgb ,
. handle = info - > handle ,
2022-05-01 01:49:46 +00:00
. label = info - > label ,
. upload = {
. stream = getTransfers ( ) ,
. buffer = scratchpad ,
. levelCount = levelCount ,
. levelOffsets = levelOffsets ,
. generateMipmaps = levelCount < mipmaps
}
2022-04-30 03:38:34 +00:00
} ) ;
// Automatically create a renderable view for renderable non-volume textures
2022-04-30 16:13:21 +00:00
if ( ( info - > usage & TEXTURE_RENDER ) & & info - > type ! = TEXTURE_3D & & info - > depth < = state . limits . renderSize [ 2 ] ) {
2022-04-30 03:38:34 +00:00
if ( info - > mipmaps = = 1 ) {
texture - > renderView = texture - > gpu ;
} else {
gpu_texture_view_info view = {
. source = texture - > gpu ,
. type = GPU_TEXTURE_ARRAY ,
. layerCount = info - > depth ,
. levelCount = 1
} ;
texture - > renderView = malloc ( gpu_sizeof_texture ( ) ) ;
lovrAssert ( texture - > renderView , " Out of memory " ) ;
lovrAssert ( gpu_texture_init_view ( texture - > renderView , & view ) , " Failed to create texture view " ) ;
}
}
return texture ;
}
2022-04-30 03:56:23 +00:00
Texture * lovrTextureCreateView ( TextureViewInfo * view ) {
const TextureInfo * info = & view - > parent - > info ;
uint32_t maxDepth = info - > type = = TEXTURE_3D ? MAX ( info - > depth > > view - > levelIndex , 1 ) : info - > depth ;
lovrCheck ( ! info - > parent , " Can't nest texture views " ) ;
lovrCheck ( view - > type ! = TEXTURE_3D , " Texture views may not be volume textures " ) ;
lovrCheck ( view - > layerCount > 0 , " Texture view must have at least one layer " ) ;
lovrCheck ( view - > levelCount > 0 , " Texture view must have at least one mipmap " ) ;
lovrCheck ( view - > layerIndex + view - > layerCount < = maxDepth , " Texture view layer range exceeds depth of parent texture " ) ;
lovrCheck ( view - > levelIndex + view - > levelCount < = info - > mipmaps , " Texture view mipmap range exceeds mipmap count of parent texture " ) ;
lovrCheck ( view - > layerCount = = 1 | | view - > type ! = TEXTURE_2D , " 2D texture can only have a single layer " ) ;
lovrCheck ( view - > levelCount = = 1 | | info - > type ! = TEXTURE_3D , " Views of volume textures may only have a single mipmap level " ) ;
lovrCheck ( view - > layerCount = = 6 | | view - > type ! = TEXTURE_CUBE , " Cubemaps can only have a six layers " ) ;
Texture * texture = calloc ( 1 , sizeof ( Texture ) + gpu_sizeof_texture ( ) ) ;
lovrAssert ( texture , " Out of memory " ) ;
texture - > ref = 1 ;
texture - > gpu = ( gpu_texture * ) ( texture + 1 ) ;
texture - > info = * info ;
texture - > info . parent = view - > parent ;
texture - > info . mipmaps = view - > levelCount ;
texture - > info . width = MAX ( info - > width > > view - > levelIndex , 1 ) ;
texture - > info . height = MAX ( info - > height > > view - > levelIndex , 1 ) ;
texture - > info . depth = view - > layerCount ;
gpu_texture_init_view ( texture - > gpu , & ( gpu_texture_view_info ) {
. source = view - > parent - > gpu ,
. type = ( gpu_texture_type ) view - > type ,
. layerIndex = view - > layerIndex ,
. layerCount = view - > layerCount ,
. levelIndex = view - > levelIndex ,
. levelCount = view - > levelCount
} ) ;
if ( view - > levelCount = = 1 & & view - > type ! = TEXTURE_3D & & view - > layerCount < = 6 ) {
texture - > renderView = texture - > gpu ;
}
lovrRetain ( view - > parent ) ;
return texture ;
}
2022-04-30 03:38:34 +00:00
void lovrTextureDestroy ( void * ref ) {
Texture * texture = ref ;
2022-05-11 22:28:04 +00:00
if ( texture ! = state . window ) {
lovrRelease ( texture - > info . parent , lovrTextureDestroy ) ;
if ( texture - > renderView & & texture - > renderView ! = texture - > gpu ) gpu_texture_destroy ( texture - > renderView ) ;
if ( texture - > gpu ) gpu_texture_destroy ( texture - > gpu ) ;
}
2022-04-30 03:38:34 +00:00
free ( texture ) ;
}
2022-04-30 03:56:23 +00:00
const TextureInfo * lovrTextureGetInfo ( Texture * texture ) {
return & texture - > info ;
}
2022-05-01 22:47:17 +00:00
// Sampler
Sampler * lovrSamplerCreate ( SamplerInfo * info ) {
lovrCheck ( info - > range [ 1 ] < 0.f | | info - > range [ 1 ] > = info - > range [ 0 ] , " Invalid Sampler mipmap range " ) ;
lovrCheck ( info - > anisotropy < = state . limits . anisotropy , " Sampler anisotropy (%f) exceeds anisotropy limit (%f) " , info - > anisotropy , state . limits . anisotropy ) ;
Sampler * sampler = calloc ( 1 , sizeof ( Sampler ) + gpu_sizeof_sampler ( ) ) ;
lovrAssert ( sampler , " Out of memory " ) ;
2022-05-09 18:47:06 +00:00
sampler - > ref = 1 ;
2022-05-01 22:47:17 +00:00
sampler - > gpu = ( gpu_sampler * ) ( sampler + 1 ) ;
sampler - > info = * info ;
gpu_sampler_info gpu = {
. min = ( gpu_filter ) info - > min ,
. mag = ( gpu_filter ) info - > mag ,
. mip = ( gpu_filter ) info - > mip ,
. wrap [ 0 ] = ( gpu_wrap ) info - > wrap [ 0 ] ,
. wrap [ 1 ] = ( gpu_wrap ) info - > wrap [ 1 ] ,
. wrap [ 2 ] = ( gpu_wrap ) info - > wrap [ 2 ] ,
. compare = ( gpu_compare_mode ) info - > compare ,
. anisotropy = MIN ( info - > anisotropy , state . limits . anisotropy ) ,
. lodClamp = { info - > range [ 0 ] , info - > range [ 1 ] }
} ;
2022-05-09 18:47:06 +00:00
gpu_sampler_init ( sampler - > gpu , & gpu ) ;
2022-05-01 22:47:17 +00:00
return sampler ;
}
void lovrSamplerDestroy ( void * ref ) {
Sampler * sampler = ref ;
gpu_sampler_destroy ( sampler - > gpu ) ;
free ( sampler ) ;
}
const SamplerInfo * lovrSamplerGetInfo ( Sampler * sampler ) {
return & sampler - > info ;
}
2022-05-09 18:47:06 +00:00
// Shader
Blob * lovrGraphicsCompileShader ( ShaderStage stage , Blob * source ) {
# ifdef LOVR_USE_GLSLANG
const glslang_stage_t stages [ ] = {
[ STAGE_VERTEX ] = GLSLANG_STAGE_VERTEX ,
[ STAGE_FRAGMENT ] = GLSLANG_STAGE_FRAGMENT ,
[ STAGE_COMPUTE ] = GLSLANG_STAGE_COMPUTE
} ;
const glslang_resource_t * resource = glslang_default_resource ( ) ;
glslang_input_t input = {
. language = GLSLANG_SOURCE_GLSL ,
. stage = stages [ stage ] ,
. client = GLSLANG_CLIENT_VULKAN ,
. client_version = GLSLANG_TARGET_VULKAN_1_1 ,
. target_language = GLSLANG_TARGET_SPV ,
. target_language_version = GLSLANG_TARGET_SPV_1_3 ,
. code = source - > data ,
. default_version = 460 ,
. default_profile = GLSLANG_NO_PROFILE ,
. resource = resource
} ;
glslang_shader_t * shader = glslang_shader_create ( & input ) ;
if ( ! glslang_shader_preprocess ( shader , & input ) ) {
lovrLog ( LOG_INFO , " Could not preprocess shader: %s " , glslang_shader_get_info_log ( shader ) ) ;
return NULL ;
}
if ( ! glslang_shader_parse ( shader , & input ) ) {
lovrLog ( LOG_INFO , " Could not parse shader: %s " , glslang_shader_get_info_log ( shader ) ) ;
return NULL ;
}
glslang_program_t * program = glslang_program_create ( ) ;
glslang_program_add_shader ( program , shader ) ;
if ( ! glslang_program_link ( program , 0 ) ) {
lovrLog ( LOG_INFO , " Could not link shader: %s " , glslang_program_get_info_log ( program ) ) ;
return NULL ;
}
glslang_program_SPIRV_generate ( program , stages [ stage ] ) ;
void * words = glslang_program_SPIRV_get_ptr ( program ) ;
size_t size = glslang_program_SPIRV_get_size ( program ) * 4 ;
void * data = malloc ( size ) ;
lovrAssert ( data , " Out of memory " ) ;
memcpy ( data , words , size ) ;
Blob * blob = lovrBlobCreate ( data , size , " SPIRV " ) ;
glslang_program_delete ( program ) ;
glslang_shader_delete ( shader ) ;
return blob ;
# endif
return NULL ;
}
Shader * lovrShaderCreate ( ShaderInfo * info ) {
Shader * shader = calloc ( 1 , sizeof ( Shader ) + gpu_sizeof_shader ( ) ) ;
lovrAssert ( shader , " Out of memory " ) ;
shader - > ref = 1 ;
shader - > gpu = ( gpu_shader * ) ( shader + 1 ) ;
shader - > info = * info ;
gpu_shader_info gpu = {
. stages [ 0 ] = { info - > stages [ 0 ] - > data , info - > stages [ 0 ] - > size } ,
. stages [ 1 ] = { info - > stages [ 1 ] - > data , info - > stages [ 1 ] - > size }
} ;
gpu_shader_init ( shader - > gpu , & gpu ) ;
return shader ;
}
void lovrShaderDestroy ( void * ref ) {
Shader * shader = ref ;
gpu_shader_destroy ( shader - > gpu ) ;
free ( shader ) ;
}
const ShaderInfo * lovrShaderGetInfo ( Shader * shader ) {
return & shader - > info ;
}
2022-04-29 05:30:31 +00:00
// Pass
Pass * lovrGraphicsGetPass ( PassInfo * info ) {
beginFrame ( ) ;
Pass * pass = tempAlloc ( sizeof ( Pass ) ) ;
pass - > ref = 1 ;
pass - > info = * info ;
pass - > stream = gpu_stream_begin ( info - > label ) ;
2022-05-11 19:51:13 +00:00
if ( info - > type = = PASS_RENDER ) {
Canvas * canvas = & info - > canvas ;
const TextureInfo * main = canvas - > textures [ 0 ] ? & canvas - > textures [ 0 ] - > info : & canvas - > depth . texture - > info ;
lovrCheck ( canvas - > textures [ 0 ] | | canvas - > depth . texture , " Render pass must have at least one color or depth texture " ) ;
lovrCheck ( main - > width < = state . limits . renderSize [ 0 ] , " Render pass width (%d) exceeds the renderSize limit of this GPU (%d) " , main - > width , state . limits . renderSize [ 0 ] ) ;
lovrCheck ( main - > height < = state . limits . renderSize [ 1 ] , " Render pass height (%d) exceeds the renderSize limit of this GPU (%d) " , main - > height , state . limits . renderSize [ 1 ] ) ;
lovrCheck ( main - > depth < = state . limits . renderSize [ 2 ] , " Render pass view count (%d) exceeds the renderSize limit of this GPU (%d) " , main - > depth , state . limits . renderSize [ 2 ] ) ;
lovrCheck ( canvas - > samples = = 1 | | canvas - > samples = = 4 , " Currently, render pass sample count must be 1 or 4 " ) ;
uint32_t colorTextureCount = 0 ;
for ( uint32_t i = 0 ; i < COUNTOF ( canvas - > textures ) & & canvas - > textures [ i ] ; i + + , colorTextureCount + + ) {
const TextureInfo * texture = & canvas - > textures [ i ] - > info ;
2022-05-11 22:28:04 +00:00
bool renderable = texture - > format = = GPU_FORMAT_SURFACE | | ( state . features . formats [ texture - > format ] & GPU_FEATURE_RENDER ) ;
2022-05-11 19:51:13 +00:00
lovrCheck ( renderable , " This GPU does not support rendering to the texture format used by Canvas texture #%d " , i + 1 ) ;
lovrCheck ( texture - > usage & TEXTURE_RENDER , " Texture must be created with the 'render' flag to render to it " ) ;
lovrCheck ( texture - > width = = main - > width , " Render pass texture sizes must match " ) ;
lovrCheck ( texture - > height = = main - > height , " Render pass texture sizes must match " ) ;
lovrCheck ( texture - > depth = = main - > depth , " Render pass texture sizes must match " ) ;
lovrCheck ( texture - > samples = = main - > samples , " Render pass texture sample counts must match " ) ;
}
if ( canvas - > depth . texture | | canvas - > depth . format ) {
TextureFormat format = canvas - > depth . texture ? canvas - > depth . texture - > info . format : canvas - > depth . format ;
bool renderable = state . features . formats [ format ] & GPU_FEATURE_RENDER ;
lovrCheck ( format = = FORMAT_D16 | | format = = FORMAT_D24S8 | | format = = FORMAT_D32F , " Depth buffer must use a depth format " ) ;
lovrCheck ( renderable , " This GPU does not support depth buffers with this texture format " ) ;
if ( canvas - > depth . texture ) {
const TextureInfo * texture = & canvas - > depth . texture - > info ;
lovrCheck ( texture - > usage & TEXTURE_RENDER , " Texture must be created with the 'render' flag to render to it " ) ;
lovrCheck ( texture - > width = = main - > width , " Render pass texture sizes must match " ) ;
lovrCheck ( texture - > height = = main - > height , " Render pass texture sizes must match " ) ;
lovrCheck ( texture - > depth = = main - > depth , " Render pass texture sizes must match " ) ;
lovrCheck ( texture - > samples = = main - > samples , " Currently, depth buffer sample count must match the main render pass sample count " ) ;
}
}
gpu_canvas target = {
. size = { main - > width , main - > height }
} ;
for ( uint32_t i = 0 ; i < colorTextureCount ; i + + ) {
if ( main - > samples = = 1 & & canvas - > samples > 1 ) {
TextureFormat format = canvas - > textures [ i ] - > info . format ;
bool srgb = canvas - > textures [ i ] - > info . srgb ;
2022-05-11 22:28:04 +00:00
target . color [ i ] . texture = getAttachment ( target . size , main - > depth , format , srgb , canvas - > samples ) ;
2022-05-11 19:51:13 +00:00
target . color [ i ] . resolve = canvas - > textures [ i ] - > renderView ;
} else {
target . color [ i ] . texture = canvas - > textures [ i ] - > renderView ;
}
target . color [ i ] . load = ( gpu_load_op ) canvas - > loads [ i ] ;
target . color [ i ] . save = GPU_SAVE_OP_SAVE ;
target . color [ i ] . clear [ 0 ] = lovrMathGammaToLinear ( canvas - > clears [ i ] [ 0 ] ) ;
target . color [ i ] . clear [ 1 ] = lovrMathGammaToLinear ( canvas - > clears [ i ] [ 1 ] ) ;
target . color [ i ] . clear [ 2 ] = lovrMathGammaToLinear ( canvas - > clears [ i ] [ 2 ] ) ;
target . color [ i ] . clear [ 3 ] = canvas - > clears [ i ] [ 2 ] ;
}
if ( canvas - > depth . texture ) {
target . depth . texture = canvas - > depth . texture - > renderView ;
} else {
2022-05-11 22:28:04 +00:00
target . depth . texture = getAttachment ( target . size , main - > depth , canvas - > depth . format , false , canvas - > samples ) ;
2022-05-11 19:51:13 +00:00
}
target . depth . load = target . depth . stencilLoad = ( gpu_load_op ) canvas - > depth . load ;
target . depth . save = canvas - > depth . texture ? GPU_SAVE_OP_SAVE : GPU_SAVE_OP_DISCARD ;
target . depth . clear . depth = canvas - > depth . clear ;
gpu_render_begin ( pass - > stream , & target ) ;
}
2022-04-29 05:30:31 +00:00
return pass ;
}
void lovrPassDestroy ( void * ref ) {
//
}
const PassInfo * lovrPassGetInfo ( Pass * pass ) {
return & pass - > info ;
}
2022-05-07 00:26:38 +00:00
void lovrPassPush ( Pass * pass , StackType stack ) {
if ( stack = = STACK_TRANSFORM ) {
pass - > transform = pass - > transforms [ + + pass - > transformIndex ] ;
lovrCheck ( pass - > transformIndex < COUNTOF ( pass - > transforms ) , " Transform stack overflow (more pushes than pops?) " ) ;
mat4_init ( pass - > transforms [ pass - > transformIndex ] , pass - > transforms [ pass - > transformIndex - 1 ] ) ;
}
}
void lovrPassPop ( Pass * pass , StackType stack ) {
if ( stack = = STACK_TRANSFORM ) {
pass - > transform = pass - > transforms [ - - pass - > transformIndex ] ;
lovrCheck ( pass - > transformIndex < COUNTOF ( pass - > transforms ) , " Transform stack underflow (more pops than pushes?) " ) ;
}
}
void lovrPassOrigin ( Pass * pass ) {
mat4_identity ( pass - > transform ) ;
}
void lovrPassTranslate ( Pass * pass , vec3 translation ) {
mat4_translate ( pass - > transform , translation [ 0 ] , translation [ 1 ] , translation [ 2 ] ) ;
}
void lovrPassRotate ( Pass * pass , quat rotation ) {
mat4_rotateQuat ( pass - > transform , rotation ) ;
}
void lovrPassScale ( Pass * pass , vec3 scale ) {
mat4_scale ( pass - > transform , scale [ 0 ] , scale [ 1 ] , scale [ 2 ] ) ;
}
void lovrPassTransform ( Pass * pass , mat4 transform ) {
mat4_mul ( pass - > transform , transform ) ;
}
2022-05-11 19:50:26 +00:00
void lovrPassSetAlphaToCoverage ( Pass * pass , bool enabled ) {
pass - > pipeline - > dirty | = enabled ! = pass - > pipeline - > info . multisample . alphaToCoverage ;
pass - > pipeline - > info . multisample . alphaToCoverage = enabled ;
}
void lovrPassSetBlendMode ( Pass * pass , BlendMode mode , BlendAlphaMode alphaMode ) {
if ( mode = = BLEND_NONE ) {
pass - > pipeline - > dirty | = pass - > pipeline - > info . color [ 0 ] . blend . enabled ;
memset ( & pass - > pipeline - > info . color [ 0 ] . blend , 0 , sizeof ( gpu_blend_state ) ) ;
return ;
}
gpu_blend_state * blend = & pass - > pipeline - > info . color [ 0 ] . blend ;
switch ( mode ) {
case BLEND_ALPHA :
blend - > color . src = GPU_BLEND_SRC_ALPHA ;
blend - > color . dst = GPU_BLEND_ONE_MINUS_SRC_ALPHA ;
blend - > color . op = GPU_BLEND_ADD ;
blend - > alpha . src = GPU_BLEND_ONE ;
blend - > alpha . dst = GPU_BLEND_ONE_MINUS_SRC_ALPHA ;
blend - > alpha . op = GPU_BLEND_ADD ;
break ;
case BLEND_ADD :
blend - > color . src = GPU_BLEND_SRC_ALPHA ;
blend - > color . dst = GPU_BLEND_ONE ;
blend - > color . op = GPU_BLEND_ADD ;
blend - > alpha . src = GPU_BLEND_ZERO ;
blend - > alpha . dst = GPU_BLEND_ONE ;
blend - > alpha . op = GPU_BLEND_ADD ;
break ;
case BLEND_SUBTRACT :
blend - > color . src = GPU_BLEND_SRC_ALPHA ;
blend - > color . dst = GPU_BLEND_ONE ;
blend - > color . op = GPU_BLEND_RSUB ;
blend - > alpha . src = GPU_BLEND_ZERO ;
blend - > alpha . dst = GPU_BLEND_ONE ;
blend - > alpha . op = GPU_BLEND_RSUB ;
break ;
case BLEND_MULTIPLY :
blend - > color . src = GPU_BLEND_DST_COLOR ;
blend - > color . dst = GPU_BLEND_ZERO ;
blend - > color . op = GPU_BLEND_ADD ;
blend - > alpha . src = GPU_BLEND_DST_COLOR ;
blend - > alpha . dst = GPU_BLEND_ZERO ;
blend - > alpha . op = GPU_BLEND_ADD ;
break ;
case BLEND_LIGHTEN :
blend - > color . src = GPU_BLEND_SRC_ALPHA ;
blend - > color . dst = GPU_BLEND_ZERO ;
blend - > color . op = GPU_BLEND_MAX ;
blend - > alpha . src = GPU_BLEND_ONE ;
blend - > alpha . dst = GPU_BLEND_ZERO ;
blend - > alpha . op = GPU_BLEND_MAX ;
break ;
case BLEND_DARKEN :
blend - > color . src = GPU_BLEND_SRC_ALPHA ;
blend - > color . dst = GPU_BLEND_ZERO ;
blend - > color . op = GPU_BLEND_MIN ;
blend - > alpha . src = GPU_BLEND_ONE ;
blend - > alpha . dst = GPU_BLEND_ZERO ;
blend - > alpha . op = GPU_BLEND_MIN ;
break ;
case BLEND_SCREEN :
blend - > color . src = GPU_BLEND_SRC_ALPHA ;
blend - > color . dst = GPU_BLEND_ONE_MINUS_SRC_COLOR ;
blend - > color . op = GPU_BLEND_ADD ;
blend - > alpha . src = GPU_BLEND_ONE ;
blend - > alpha . dst = GPU_BLEND_ONE_MINUS_SRC_COLOR ;
blend - > alpha . op = GPU_BLEND_ADD ;
break ;
default : lovrUnreachable ( ) ;
} ;
if ( alphaMode = = BLEND_PREMULTIPLIED & & mode ! = BLEND_MULTIPLY ) {
blend - > color . src = GPU_BLEND_ONE ;
}
blend - > enabled = true ;
pass - > pipeline - > dirty = true ;
}
void lovrPassSetColorMask ( Pass * pass , bool r , bool g , bool b , bool a ) {
uint8_t mask = ( r < < 0 ) | ( g < < 1 ) | ( b < < 2 ) | ( a < < 3 ) ;
pass - > pipeline - > dirty | = pass - > pipeline - > info . color [ 0 ] . mask ! = mask ;
pass - > pipeline - > info . color [ 0 ] . mask = mask ;
}
void lovrPassSetCullMode ( Pass * pass , CullMode mode ) {
pass - > pipeline - > dirty | = pass - > pipeline - > info . rasterizer . cullMode ! = ( gpu_cull_mode ) mode ;
pass - > pipeline - > info . rasterizer . cullMode = ( gpu_cull_mode ) mode ;
}
void lovrPassSetDepthTest ( Pass * pass , CompareMode test ) {
pass - > pipeline - > dirty | = pass - > pipeline - > info . depth . test ! = ( gpu_compare_mode ) test ;
pass - > pipeline - > info . depth . test = ( gpu_compare_mode ) test ;
}
void lovrPassSetDepthWrite ( Pass * pass , bool write ) {
pass - > pipeline - > dirty | = pass - > pipeline - > info . depth . write ! = write ;
pass - > pipeline - > info . depth . write = write ;
}
void lovrPassSetDepthOffset ( Pass * pass , float offset , float sloped ) {
pass - > pipeline - > info . rasterizer . depthOffset = offset ;
pass - > pipeline - > info . rasterizer . depthOffsetSloped = sloped ;
pass - > pipeline - > dirty = true ;
}
void lovrPassSetDepthClamp ( Pass * pass , bool clamp ) {
if ( state . features . depthClamp ) {
pass - > pipeline - > dirty | = pass - > pipeline - > info . rasterizer . depthClamp ! = clamp ;
pass - > pipeline - > info . rasterizer . depthClamp = clamp ;
}
}
void lovrPassSetShader ( Pass * pass , Shader * shader ) {
lovrRetain ( shader ) ;
lovrRelease ( pass - > pipeline - > shader , lovrShaderDestroy ) ;
pass - > pipeline - > shader = shader ;
pass - > pipeline - > info . shader = shader ? shader - > gpu : NULL ;
pass - > pipeline - > dirty = true ;
}
void lovrPassSetStencilTest ( Pass * pass , CompareMode test , uint8_t value , uint8_t mask ) {
bool hasReplace = false ;
hasReplace | = pass - > pipeline - > info . stencil . failOp = = GPU_STENCIL_REPLACE ;
hasReplace | = pass - > pipeline - > info . stencil . depthFailOp = = GPU_STENCIL_REPLACE ;
hasReplace | = pass - > pipeline - > info . stencil . passOp = = GPU_STENCIL_REPLACE ;
if ( hasReplace & & test ! = COMPARE_NONE ) {
lovrCheck ( value = = pass - > pipeline - > info . stencil . value , " When stencil write is 'replace' and stencil test is active, their values must match " ) ;
}
switch ( test ) { // (Reversed compare mode)
case COMPARE_NONE : default : pass - > pipeline - > info . stencil . test = GPU_COMPARE_NONE ; break ;
case COMPARE_EQUAL : pass - > pipeline - > info . stencil . test = GPU_COMPARE_EQUAL ; break ;
case COMPARE_NEQUAL : pass - > pipeline - > info . stencil . test = GPU_COMPARE_NEQUAL ; break ;
case COMPARE_LESS : pass - > pipeline - > info . stencil . test = GPU_COMPARE_GREATER ; break ;
case COMPARE_LEQUAL : pass - > pipeline - > info . stencil . test = GPU_COMPARE_GEQUAL ; break ;
case COMPARE_GREATER : pass - > pipeline - > info . stencil . test = GPU_COMPARE_LESS ; break ;
case COMPARE_GEQUAL : pass - > pipeline - > info . stencil . test = GPU_COMPARE_LEQUAL ; break ;
}
pass - > pipeline - > info . stencil . testMask = mask ;
if ( test ! = COMPARE_NONE ) pass - > pipeline - > info . stencil . value = value ;
pass - > pipeline - > dirty = true ;
}
void lovrPassSetStencilWrite ( Pass * pass , StencilAction actions [ 3 ] , uint8_t value , uint8_t mask ) {
bool hasReplace = actions [ 0 ] = = STENCIL_REPLACE | | actions [ 1 ] = = STENCIL_REPLACE | | actions [ 2 ] = = STENCIL_REPLACE ;
if ( hasReplace & & pass - > pipeline - > info . stencil . test ! = GPU_COMPARE_NONE ) {
lovrCheck ( value = = pass - > pipeline - > info . stencil . value , " When stencil write is 'replace' and stencil test is active, their values must match " ) ;
}
pass - > pipeline - > info . stencil . failOp = ( gpu_stencil_op ) actions [ 0 ] ;
pass - > pipeline - > info . stencil . depthFailOp = ( gpu_stencil_op ) actions [ 1 ] ;
pass - > pipeline - > info . stencil . passOp = ( gpu_stencil_op ) actions [ 2 ] ;
pass - > pipeline - > info . stencil . writeMask = mask ;
if ( hasReplace ) pass - > pipeline - > info . stencil . value = value ;
pass - > pipeline - > dirty = true ;
}
void lovrPassSetWinding ( Pass * pass , Winding winding ) {
pass - > pipeline - > dirty | = pass - > pipeline - > info . rasterizer . winding ! = ( gpu_winding ) winding ;
pass - > pipeline - > info . rasterizer . winding = ( gpu_winding ) winding ;
}
void lovrPassSetWireframe ( Pass * pass , bool wireframe ) {
if ( state . features . wireframe ) {
pass - > pipeline - > dirty | = pass - > pipeline - > info . rasterizer . wireframe ! = ( gpu_winding ) wireframe ;
pass - > pipeline - > info . rasterizer . wireframe = wireframe ;
}
}
2022-04-21 07:27:13 +00:00
// Helpers
2022-04-29 05:30:31 +00:00
static void * tempAlloc ( size_t size ) {
2022-04-27 05:44:44 +00:00
while ( state . allocator . cursor + size > state . allocator . length ) {
lovrAssert ( state . allocator . length < < 1 < = MAX_FRAME_MEMORY , " Out of memory " ) ;
os_vm_commit ( state . allocator . memory + state . allocator . length , state . allocator . length ) ;
state . allocator . length < < = 1 ;
}
uint32_t cursor = ALIGN ( state . allocator . cursor , 8 ) ;
state . allocator . cursor = cursor + size ;
return state . allocator . memory + cursor ;
}
2022-04-29 05:30:31 +00:00
static void beginFrame ( void ) {
if ( state . active ) {
return ;
}
state . active = true ;
state . tick = gpu_begin ( ) ;
}
static gpu_stream * getTransfers ( void ) {
if ( ! state . transfers ) {
state . transfers = lovrGraphicsGetPass ( & ( PassInfo ) {
. type = PASS_TRANSFER ,
. label = " Internal Transfers "
} ) ;
}
return state . transfers - > stream ;
}
2022-05-11 22:28:04 +00:00
static gpu_texture * getAttachment ( uint32_t size [ 2 ] , uint32_t layers , TextureFormat format , bool srgb , uint32_t samples ) {
2022-05-11 19:51:13 +00:00
uint16_t key [ ] = { size [ 0 ] , size [ 1 ] , layers , format , srgb , samples } ;
uint32_t hash = hash64 ( key , sizeof ( key ) ) ;
2022-05-11 22:28:04 +00:00
Attachment * attachment = state . attachments ;
for ( uint32_t i = 0 ; i < COUNTOF ( state . attachments ) & & attachment - > texture ; i + + , attachment + + ) {
if ( attachment - > hash = = hash & & attachment - > tick ! = state . tick ) {
attachment - > tick = state . tick ;
return attachment - > texture ;
2022-05-11 19:51:13 +00:00
}
}
// Otherwise, create new texture, add to an empty slot, evicting oldest if needed
gpu_texture_info info = {
. type = GPU_TEXTURE_ARRAY ,
. format = ( gpu_texture_format ) format ,
. size [ 0 ] = size [ 0 ] ,
. size [ 1 ] = size [ 1 ] ,
. size [ 2 ] = layers ,
. mipmaps = 1 ,
. samples = samples ,
. usage = GPU_TEXTURE_RENDER | GPU_TEXTURE_TRANSIENT ,
. upload . stream = getTransfers ( ) ,
. srgb = srgb
} ;
uint32_t oldest = ~ 0u ;
2022-05-11 22:28:04 +00:00
for ( uint32_t i = 0 ; i < COUNTOF ( state . attachments ) ; i + + ) {
if ( ! state . attachments [ i ] . texture ) {
attachment = & state . attachments [ i ] ;
2022-05-11 19:51:13 +00:00
break ;
2022-05-11 22:28:04 +00:00
} else if ( state . attachments [ i ] . tick < oldest ) {
attachment = & state . attachments [ i ] ;
oldest = attachment - > tick ;
2022-05-11 19:51:13 +00:00
}
}
2022-05-11 22:28:04 +00:00
if ( ! attachment - > texture ) {
attachment - > texture = calloc ( 1 , gpu_sizeof_texture ( ) ) ;
lovrAssert ( attachment - > texture , " Out of memory " ) ;
2022-05-11 19:51:13 +00:00
} else {
2022-05-11 22:28:04 +00:00
gpu_texture_destroy ( attachment - > texture ) ;
2022-05-11 19:51:13 +00:00
}
2022-05-11 22:28:04 +00:00
lovrAssert ( gpu_texture_init ( attachment - > texture , & info ) , " Failed to create scratch texture " ) ;
attachment - > hash = hash ;
attachment - > tick = state . tick ;
return attachment - > texture ;
2022-05-11 19:51:13 +00:00
}
2022-04-30 03:38:34 +00:00
// Returns number of bytes of a 3D texture region of a given format
static size_t measureTexture ( TextureFormat format , uint16_t w , uint16_t h , uint16_t d ) {
switch ( format ) {
case FORMAT_R8 : return w * h * d ;
case FORMAT_RG8 :
case FORMAT_R16 :
case FORMAT_R16F :
case FORMAT_RGB565 :
case FORMAT_RGB5A1 :
case FORMAT_D16 : return w * h * d * 2 ;
case FORMAT_RGBA8 :
case FORMAT_RG16 :
case FORMAT_RG16F :
case FORMAT_R32F :
case FORMAT_RG11B10F :
case FORMAT_RGB10A2 :
case FORMAT_D24S8 :
case FORMAT_D32F : return w * h * d * 4 ;
case FORMAT_RGBA16 :
case FORMAT_RGBA16F :
case FORMAT_RG32F : return w * h * d * 8 ;
case FORMAT_RGBA32F : return w * h * d * 16 ;
case FORMAT_BC1 :
case FORMAT_BC2 :
case FORMAT_BC3 :
case FORMAT_BC4U :
case FORMAT_BC4S :
case FORMAT_BC5U :
case FORMAT_BC5S :
case FORMAT_BC6UF :
case FORMAT_BC6SF :
case FORMAT_BC7 :
case FORMAT_ASTC_4x4 : return ( ( w + 3 ) / 4 ) * ( ( h + 3 ) / 4 ) * d * 16 ;
case FORMAT_ASTC_5x4 : return ( ( w + 4 ) / 5 ) * ( ( h + 3 ) / 4 ) * d * 16 ;
case FORMAT_ASTC_5x5 : return ( ( w + 4 ) / 5 ) * ( ( h + 4 ) / 5 ) * d * 16 ;
case FORMAT_ASTC_6x5 : return ( ( w + 5 ) / 6 ) * ( ( h + 4 ) / 5 ) * d * 16 ;
case FORMAT_ASTC_6x6 : return ( ( w + 5 ) / 6 ) * ( ( h + 5 ) / 6 ) * d * 16 ;
case FORMAT_ASTC_8x5 : return ( ( w + 7 ) / 8 ) * ( ( h + 4 ) / 5 ) * d * 16 ;
case FORMAT_ASTC_8x6 : return ( ( w + 7 ) / 8 ) * ( ( h + 5 ) / 6 ) * d * 16 ;
case FORMAT_ASTC_8x8 : return ( ( w + 7 ) / 8 ) * ( ( h + 7 ) / 8 ) * d * 16 ;
case FORMAT_ASTC_10x5 : return ( ( w + 9 ) / 10 ) * ( ( h + 4 ) / 5 ) * d * 16 ;
case FORMAT_ASTC_10x6 : return ( ( w + 9 ) / 10 ) * ( ( h + 5 ) / 6 ) * d * 16 ;
case FORMAT_ASTC_10x8 : return ( ( w + 9 ) / 10 ) * ( ( h + 7 ) / 8 ) * d * 16 ;
case FORMAT_ASTC_10x10 : return ( ( w + 9 ) / 10 ) * ( ( h + 9 ) / 10 ) * d * 16 ;
case FORMAT_ASTC_12x10 : return ( ( w + 11 ) / 12 ) * ( ( h + 9 ) / 10 ) * d * 16 ;
case FORMAT_ASTC_12x12 : return ( ( w + 11 ) / 12 ) * ( ( h + 11 ) / 12 ) * d * 16 ;
default : lovrUnreachable ( ) ;
}
}
2022-04-21 07:27:13 +00:00
static void onMessage ( void * context , const char * message , bool severe ) {
if ( severe ) {
lovrLog ( LOG_ERROR , " GPU " , message ) ;
} else {
lovrLog ( LOG_DEBUG , " GPU " , message ) ;
}
}