diff --git a/src/api/l_graphics.c b/src/api/l_graphics.c index 31176cda1..698241ab9 100644 --- a/src/api/l_graphics.c +++ b/src/api/l_graphics.c @@ -101,9 +101,17 @@ StringEntry lovrDrawStyle[] = { }; StringEntry lovrDataType[] = { + [TYPE_I8] = ENTRY("i8"), + [TYPE_I8x2] = ENTRY("i8x2"), [TYPE_I8x4] = ENTRY("i8x4"), + [TYPE_U8] = ENTRY("u8"), + [TYPE_U8x2] = ENTRY("u8x2"), [TYPE_U8x4] = ENTRY("u8x4"), + [TYPE_SN8] = ENTRY("sn8"), + [TYPE_SN8x2] = ENTRY("sn8x2"), [TYPE_SN8x4] = ENTRY("sn8x4"), + [TYPE_UN8] = ENTRY("un8"), + [TYPE_UN8x2] = ENTRY("un8x2"), [TYPE_UN8x4] = ENTRY("un8x4"), [TYPE_SN10x3] = ENTRY("sn10x3"), [TYPE_UN10x3] = ENTRY("un10x3"), @@ -113,8 +121,10 @@ StringEntry lovrDataType[] = { [TYPE_U16] = ENTRY("u16"), [TYPE_U16x2] = ENTRY("u16x2"), [TYPE_U16x4] = ENTRY("u16x4"), + [TYPE_SN16] = ENTRY("sn16"), [TYPE_SN16x2] = ENTRY("sn16x2"), [TYPE_SN16x4] = ENTRY("sn16x4"), + [TYPE_UN16] = ENTRY("un16"), [TYPE_UN16x2] = ENTRY("un16x2"), [TYPE_UN16x4] = ENTRY("un16x4"), [TYPE_I32] = ENTRY("i32"), @@ -125,6 +135,7 @@ StringEntry lovrDataType[] = { [TYPE_U32x2] = ENTRY("u32x2"), [TYPE_U32x3] = ENTRY("u32x3"), [TYPE_U32x4] = ENTRY("u32x4"), + [TYPE_F16] = ENTRY("f16"), [TYPE_F16x2] = ENTRY("f16x2"), [TYPE_F16x4] = ENTRY("f16x4"), [TYPE_F32] = ENTRY("f32"), @@ -582,8 +593,10 @@ static int l_lovrGraphicsGetFeatures(lua_State* L) { lua_pushboolean(L, features.indirectDrawFirstInstance), lua_setfield(L, -2, "indirectDrawFirstInstance"); lua_pushboolean(L, features.packedBuffers), lua_setfield(L, -2, "packedBuffers"); lua_pushboolean(L, features.float64), lua_setfield(L, -2, "float64"); + lua_pushboolean(L, features.float16), lua_setfield(L, -2, "float16"); lua_pushboolean(L, features.int64), lua_setfield(L, -2, "int64"); lua_pushboolean(L, features.int16), lua_setfield(L, -2, "int16"); + lua_pushboolean(L, features.int8), lua_setfield(L, -2, "int8"); return 1; } diff --git a/src/api/l_graphics_buffer.c b/src/api/l_graphics_buffer.c index 5c7264158..649323057 100644 --- a/src/api/l_graphics_buffer.c +++ b/src/api/l_graphics_buffer.c @@ -7,9 +7,17 @@ #include static const uint32_t typeComponents[] = { + [TYPE_I8] = 1, + [TYPE_I8x2] = 2, [TYPE_I8x4] = 4, + [TYPE_U8] = 1, + [TYPE_U8x2] = 2, [TYPE_U8x4] = 4, + [TYPE_SN8] = 1, + [TYPE_SN8x2] = 2, [TYPE_SN8x4] = 4, + [TYPE_UN8] = 1, + [TYPE_UN8x2] = 2, [TYPE_UN8x4] = 4, [TYPE_SN10x3] = 3, [TYPE_UN10x3] = 3, @@ -19,8 +27,10 @@ static const uint32_t typeComponents[] = { [TYPE_U16] = 1, [TYPE_U16x2] = 2, [TYPE_U16x4] = 4, + [TYPE_SN16] = 1, [TYPE_SN16x2] = 2, [TYPE_SN16x4] = 4, + [TYPE_UN16] = 1, [TYPE_UN16x2] = 2, [TYPE_UN16x4] = 4, [TYPE_I32] = 1, @@ -31,6 +41,7 @@ static const uint32_t typeComponents[] = { [TYPE_U32x2] = 2, [TYPE_U32x3] = 3, [TYPE_U32x4] = 4, + [TYPE_F16] = 1, [TYPE_F16x2] = 2, [TYPE_F16x4] = 4, [TYPE_F32] = 1, @@ -120,9 +131,17 @@ static bool luax_checkfieldn(lua_State* L, int index, const DataField* field, vo luax_fieldcheck(L, lua_type(L, index + i) == LUA_TNUMBER, index + i, field, false); double x = lua_tonumber(L, index + i); switch (field->type) { + case TYPE_I8: p.i8[i] = (int8_t) x; break; + case TYPE_I8x2: p.i8[i] = (int8_t) x; break; case TYPE_I8x4: p.i8[i] = (int8_t) x; break; + case TYPE_U8: p.u8[i] = (uint8_t) x; break; + case TYPE_U8x2: p.u8[i] = (uint8_t) x; break; case TYPE_U8x4: p.u8[i] = (uint8_t) x; break; + case TYPE_SN8: p.i8[i] = (int8_t)(CLAMP(x, -1.f, 1.f) * INT8_MAX); break; + case TYPE_SN8x2: p.i8[i] = (int8_t)(CLAMP(x, -1.f, 1.f) * INT8_MAX); break; case TYPE_SN8x4: p.i8[i] = (int8_t) (CLAMP(x, -1.f, 1.f) * INT8_MAX); break; + case TYPE_UN8: p.u8[i] = (uint8_t)(CLAMP(x, 0.f, 1.f) * UINT8_MAX); break; + case TYPE_UN8x2: p.u8[i] = (uint8_t)(CLAMP(x, 0.f, 1.f) * UINT8_MAX); break; case TYPE_UN8x4: p.u8[i] = (uint8_t) (CLAMP(x, 0.f, 1.f) * UINT8_MAX); break; case TYPE_SN10x3: p.u32[0] |= (((uint32_t) (int32_t) (CLAMP(x, -1.f, 1.f) * 511.f)) & 0x3ff) << (10 * i); break; case TYPE_UN10x3: p.u32[0] |= (((uint32_t) (CLAMP(x, 0.f, 1.f) * 1023.f)) & 0x3ff) << (10 * i); break; @@ -132,8 +151,10 @@ static bool luax_checkfieldn(lua_State* L, int index, const DataField* field, vo case TYPE_U16: p.u16[i] = (uint16_t) x; break; case TYPE_U16x2: p.u16[i] = (uint16_t) x; break; case TYPE_U16x4: p.u16[i] = (uint16_t) x; break; + case TYPE_SN16: p.i16[i] = (int16_t)(CLAMP(x, -1.f, 1.f) * INT16_MAX); break; case TYPE_SN16x2: p.i16[i] = (int16_t) (CLAMP(x, -1.f, 1.f) * INT16_MAX); break; case TYPE_SN16x4: p.i16[i] = (int16_t) (CLAMP(x, -1.f, 1.f) * INT16_MAX); break; + case TYPE_UN16: p.u16[i] = (uint16_t)(CLAMP(x, 0.f, 1.f) * UINT16_MAX); break; case TYPE_UN16x2: p.u16[i] = (uint16_t) (CLAMP(x, 0.f, 1.f) * UINT16_MAX); break; case TYPE_UN16x4: p.u16[i] = (uint16_t) (CLAMP(x, 0.f, 1.f) * UINT16_MAX); break; case TYPE_I32: p.i32[i] = (int32_t) x; break; @@ -143,7 +164,8 @@ static bool luax_checkfieldn(lua_State* L, int index, const DataField* field, vo case TYPE_U32: p.u32[i] = (uint32_t) x; break; case TYPE_U32x2: p.u32[i] = (uint32_t) x; break; case TYPE_U32x3: p.u32[i] = (uint32_t) x; break; - case TYPE_U32x4: p.i32[i] = (uint32_t) x; break; + case TYPE_U32x4: p.u32[i] = (uint32_t) x; break; + case TYPE_F16: p.u16[i] = float32to16(x); break; case TYPE_F16x2: p.u16[i] = float32to16(x); break; case TYPE_F16x4: p.u16[i] = float32to16(x); break; case TYPE_F32: p.f32[i] = (float) x; break; diff --git a/src/core/gpu.h b/src/core/gpu.h index dde143793..664f718a9 100644 --- a/src/core/gpu.h +++ b/src/core/gpu.h @@ -57,9 +57,17 @@ void gpu_buffer_flush(gpu_buffer* buffer, uint32_t offset, uint32_t extent); // Tree typedef enum { + GPU_TYPE_I8, + GPU_TYPE_I8x2, GPU_TYPE_I8x4, + GPU_TYPE_U8, + GPU_TYPE_U8x2, GPU_TYPE_U8x4, + GPU_TYPE_SN8, + GPU_TYPE_SN8x2, GPU_TYPE_SN8x4, + GPU_TYPE_UN8, + GPU_TYPE_UN8x2, GPU_TYPE_UN8x4, GPU_TYPE_SN10x3, GPU_TYPE_UN10x3, @@ -69,8 +77,10 @@ typedef enum { GPU_TYPE_U16, GPU_TYPE_U16x2, GPU_TYPE_U16x4, + GPU_TYPE_SN16, GPU_TYPE_SN16x2, GPU_TYPE_SN16x4, + GPU_TYPE_UN16, GPU_TYPE_UN16x2, GPU_TYPE_UN16x4, GPU_TYPE_I32, @@ -81,6 +91,7 @@ typedef enum { GPU_TYPE_U32x2, GPU_TYPE_U32x3, GPU_TYPE_U32x4, + GPU_TYPE_F16, GPU_TYPE_F16x2, GPU_TYPE_F16x4, GPU_TYPE_F32, @@ -807,8 +818,10 @@ typedef struct { bool subgroupQuad; bool float32AtomicAdd; bool float64; + bool float16; bool int64; bool int16; + bool int8; } gpu_features; typedef struct { diff --git a/src/core/gpu_vk.c b/src/core/gpu_vk.c index 788c6ebab..33bd7c58d 100644 --- a/src/core/gpu_vk.c +++ b/src/core/gpu_vk.c @@ -244,6 +244,9 @@ typedef struct { bool hostImageCopy; bool atomicFloat; bool calibratedTimestamps; + bool float16Int8; + bool storage8; + bool storage16; } gpu_extensions; // State @@ -3125,7 +3128,10 @@ bool gpu_init(gpu_config* config) { { "VK_EXT_pipeline_creation_cache_control", true, &state.extensions.pipelineCacheControl }, { "VK_EXT_memory_budget", true, &state.extensions.memoryBudget }, { "VK_EXT_host_image_copy", true, &state.extensions.hostImageCopy }, - { "VK_EXT_shader_atomic_float", true, &state.extensions.atomicFloat } + { "VK_EXT_shader_atomic_float", true, &state.extensions.atomicFloat }, + { "VK_KHR_shader_float16_int8", true, &state.extensions.float16Int8 }, + { "VK_KHR_8bit_storage", true, &state.extensions.storage8 }, + { "VK_KHR_16bit_storage", true, &state.extensions.storage16 } }; uint32_t extensionCount = 0; @@ -3228,6 +3234,9 @@ bool gpu_init(gpu_config* config) { VkPhysicalDeviceRayQueryFeaturesKHR rayQueryFeatures = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_QUERY_FEATURES_KHR }; VkPhysicalDeviceHostImageCopyFeaturesEXT hostImageCopyFeatures = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_IMAGE_COPY_FEATURES_EXT }; VkPhysicalDeviceShaderAtomicFloatFeaturesEXT atomicFloatFeatures = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_FLOAT_FEATURES_EXT }; + VkPhysicalDeviceShaderFloat16Int8Features float16int8 = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES }; + VkPhysicalDevice8BitStorageFeatures storage8 = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES }; + VkPhysicalDevice16BitStorageFeatures storage16 = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES }; if (state.extensions.foveation) { CHAIN(supported, fragmentDensityMapFeatures); @@ -3237,6 +3246,18 @@ bool gpu_init(gpu_config* config) { CHAIN(supported, atomicFloatFeatures); } + if (state.extensions.float16Int8) { + CHAIN(supported, float16int8); + } + + if (state.extensions.storage8) { + CHAIN(supported, storage8); + } + + if (state.extensions.storage16) { + CHAIN(supported, storage16); + } + vkGetPhysicalDeviceFeatures2(state.adapter, &supported); VkPhysicalDeviceFeatures2 enabled = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2 }; @@ -3315,6 +3336,18 @@ bool gpu_init(gpu_config* config) { CHAIN(enabled, atomicFloatFeatures); } + if (state.extensions.float16Int8) { + CHAIN(enabled, float16int8); + } + + if (state.extensions.storage8) { + CHAIN(enabled, storage8); + } + + if (state.extensions.storage16) { + CHAIN(enabled, storage16); + } + if (config->features) { config->features->textureBC = enabled.features.textureCompressionBC; config->features->textureASTC = enabled.features.textureCompressionASTC_LDR; @@ -3335,8 +3368,10 @@ bool gpu_init(gpu_config* config) { config->features->subgroupQuad = subgroupProperties.supportedOperations & VK_SUBGROUP_FEATURE_QUAD_BIT; config->features->float32AtomicAdd = atomicFloatFeatures.shaderBufferFloat32AtomicAdd && atomicFloatFeatures.shaderImageFloat32AtomicAdd; config->features->float64 = enabled.features.shaderFloat64; + config->features->float16 = float16int8.shaderFloat16 && storage16.storageBuffer16BitAccess; config->features->int64 = enabled.features.shaderInt64; - config->features->int16 = enabled.features.shaderInt16; + config->features->int16 = enabled.features.shaderInt16 && storage16.storageBuffer16BitAccess; + config->features->int8 = float16int8.shaderInt8 && storage8.storageBuffer8BitAccess; // Formats for (uint32_t i = 0; i < GPU_FORMAT_COUNT; i++) { @@ -4349,9 +4384,17 @@ static VkFormat convertFormat(gpu_texture_format format, int colorspace) { static VkFormat convertAttributeType(gpu_attribute_type type) { static const VkFormat types[] = { + [GPU_TYPE_I8] = VK_FORMAT_R8_SINT, + [GPU_TYPE_I8x2] = VK_FORMAT_R8G8_SINT, [GPU_TYPE_I8x4] = VK_FORMAT_R8G8B8A8_SINT, + [GPU_TYPE_U8] = VK_FORMAT_R8_UINT, + [GPU_TYPE_U8x2] = VK_FORMAT_R8G8_UINT, [GPU_TYPE_U8x4] = VK_FORMAT_R8G8B8A8_UINT, + [GPU_TYPE_SN8] = VK_FORMAT_R8_SNORM, + [GPU_TYPE_SN8x2] = VK_FORMAT_R8G8_SNORM, [GPU_TYPE_SN8x4] = VK_FORMAT_R8G8B8A8_SNORM, + [GPU_TYPE_UN8] = VK_FORMAT_R8_UNORM, + [GPU_TYPE_UN8x2] = VK_FORMAT_R8G8_UNORM, [GPU_TYPE_UN8x4] = VK_FORMAT_R8G8B8A8_UNORM, [GPU_TYPE_SN10x3] = VK_FORMAT_A2B10G10R10_SNORM_PACK32, [GPU_TYPE_UN10x3] = VK_FORMAT_A2B10G10R10_UNORM_PACK32, @@ -4361,8 +4404,10 @@ static VkFormat convertAttributeType(gpu_attribute_type type) { [GPU_TYPE_U16] = VK_FORMAT_R16_UINT, [GPU_TYPE_U16x2] = VK_FORMAT_R16G16_UINT, [GPU_TYPE_U16x4] = VK_FORMAT_R16G16B16A16_UINT, + [GPU_TYPE_SN16] = VK_FORMAT_R16_SNORM, [GPU_TYPE_SN16x2] = VK_FORMAT_R16G16_SNORM, [GPU_TYPE_SN16x4] = VK_FORMAT_R16G16B16A16_SNORM, + [GPU_TYPE_UN16] = VK_FORMAT_R16_UNORM, [GPU_TYPE_UN16x2] = VK_FORMAT_R16G16_UNORM, [GPU_TYPE_UN16x4] = VK_FORMAT_R16G16B16A16_UNORM, [GPU_TYPE_I32] = VK_FORMAT_R32_SINT, @@ -4373,6 +4418,7 @@ static VkFormat convertAttributeType(gpu_attribute_type type) { [GPU_TYPE_U32x2] = VK_FORMAT_R32G32_UINT, [GPU_TYPE_U32x3] = VK_FORMAT_R32G32B32_UINT, [GPU_TYPE_U32x4] = VK_FORMAT_R32G32B32A32_UINT, + [GPU_TYPE_F16] = VK_FORMAT_R16_SFLOAT, [GPU_TYPE_F16x2] = VK_FORMAT_R16G16_SFLOAT, [GPU_TYPE_F16x4] = VK_FORMAT_R16G16B16A16_SFLOAT, [GPU_TYPE_F32] = VK_FORMAT_R32_SFLOAT, diff --git a/src/core/gpu_web.c b/src/core/gpu_web.c index dbc7dbcf2..cfb62f7c9 100644 --- a/src/core/gpu_web.c +++ b/src/core/gpu_web.c @@ -645,9 +645,17 @@ bool gpu_pipeline_init_graphics(gpu_pipeline* pipeline, gpu_pipeline_info* info, }; static const WGPU_VERTEX_FORMAT attributeTypes[] = { + [GPU_TYPE_I8] = WGPU_VERTEX_FORMAT_SINT8, + [GPU_TYPE_I8x2] = WGPU_VERTEX_FORMAT_SINT8X2, [GPU_TYPE_I8x4] = WGPU_VERTEX_FORMAT_SINT8X4, + [GPU_TYPE_U8] = WGPU_VERTEX_FORMAT_UINT8, + [GPU_TYPE_U8x2] = WGPU_VERTEX_FORMAT_UINT8X2, [GPU_TYPE_U8x4] = WGPU_VERTEX_FORMAT_UINT8X4, + [GPU_TYPE_SN8] = WGPU_VERTEX_FORMAT_SNORM8, + [GPU_TYPE_SN8x2] = WGPU_VERTEX_FORMAT_SNORM8X2, [GPU_TYPE_SN8x4] = WGPU_VERTEX_FORMAT_SNORM8X4, + [GPU_TYPE_UN8] = WGPU_VERTEX_FORMAT_UNORM8, + [GPU_TYPE_UN8x2] = WGPU_VERTEX_FORMAT_UNORM8X2, [GPU_TYPE_UN8x4] = WGPU_VERTEX_FORMAT_UNORM8X4, [GPU_TYPE_SN10x3] = WGPU_VERTEX_FORMAT_UNORM10_10_10_2, // TODO [GPU_TYPE_UN10x3] = WGPU_VERTEX_FORMAT_UNORM10_10_10_2, @@ -657,8 +665,10 @@ bool gpu_pipeline_init_graphics(gpu_pipeline* pipeline, gpu_pipeline_info* info, [GPU_TYPE_U16] = WGPU_VERTEX_FORMAT_UINT16, [GPU_TYPE_U16x2] = WGPU_VERTEX_FORMAT_UINT16X2, [GPU_TYPE_U16x4] = WGPU_VERTEX_FORMAT_UINT16X4, + [GPU_TYPE_SN16] = WGPU_VERTEX_FORMAT_SNORM16, [GPU_TYPE_SN16x2] = WGPU_VERTEX_FORMAT_SNORM16X2, [GPU_TYPE_SN16x4] = WGPU_VERTEX_FORMAT_SNORM16X4, + [GPU_TYPE_UN16] = WGPU_VERTEX_FORMAT_UNORM16, [GPU_TYPE_UN16x2] = WGPU_VERTEX_FORMAT_UNORM16X2, [GPU_TYPE_UN16x4] = WGPU_VERTEX_FORMAT_UNORM16X4, [GPU_TYPE_I32] = WGPU_VERTEX_FORMAT_SINT32, @@ -669,6 +679,7 @@ bool gpu_pipeline_init_graphics(gpu_pipeline* pipeline, gpu_pipeline_info* info, [GPU_TYPE_U32x2] = WGPU_VERTEX_FORMAT_UINT32X2, [GPU_TYPE_U32x3] = WGPU_VERTEX_FORMAT_UINT32X3, [GPU_TYPE_U32x4] = WGPU_VERTEX_FORMAT_UINT32X4, + [GPU_TYPE_F16] = WGPU_VERTEX_FORMAT_FLOAT16, [GPU_TYPE_F16x2] = WGPU_VERTEX_FORMAT_FLOAT16X2, [GPU_TYPE_F16x4] = WGPU_VERTEX_FORMAT_FLOAT16X4, [GPU_TYPE_F32] = WGPU_VERTEX_FORMAT_FLOAT32, @@ -1486,8 +1497,10 @@ bool gpu_init(gpu_config* config) { config->features->subgroupQuad = false; config->features->float32AtomicAdd = false; config->features->float64 = false; + config->features->float16 = wgpu_device_supports_feature(state.device, WGPU_FEATURE_SHADER_F16); config->features->int64 = false; config->features->int16 = false; + config->features->int8 = false; config->features->formats[GPU_FORMAT_R8][0] = GPU_FEATURE_SAMPLE | GPU_FEATURE_RENDER | GPU_FEATURE_BLIT; config->features->formats[GPU_FORMAT_RG8][0] = GPU_FEATURE_SAMPLE | GPU_FEATURE_RENDER | GPU_FEATURE_BLIT; diff --git a/src/core/spv.c b/src/core/spv.c index 77a0262b7..16d6598d1 100644 --- a/src/core/spv.c +++ b/src/core/spv.c @@ -733,7 +733,13 @@ static spv_result spv_parse_field(spv_context* spv, const uint32_t* word, spv_fi } } - if (OP_CODE(word) == 22 && word[2] == 32) { // OpTypeFloat + if (OP_CODE(word) == 22 && word[2] == 16) { // OpTypeFloat 16-bit + if (columnCount == 1 && componentCount <= 4 && componentCount != 3) { + field->type = SPV_F16 + (componentCount / 2); + } else { + return SPV_UNSUPPORTED_DATA_TYPE; + } + } else if (OP_CODE(word) == 22 && word[2] == 32) { // OpTypeFloat 32-bit if (columnCount >= 2 && columnCount <= 4 && componentCount >= 2 && componentCount <= 4) { field->type = SPV_MAT2x2 + (columnCount - 2) * 3 + (componentCount - 2); } else if (columnCount == 1 && componentCount >= 2 && componentCount <= 4) { @@ -743,7 +749,35 @@ static spv_result spv_parse_field(spv_context* spv, const uint32_t* word, spv_fi } else { return SPV_UNSUPPORTED_DATA_TYPE; } - } else if (OP_CODE(word) == 21 && word[2] == 32) { // OpTypeInteger + } else if (OP_CODE(word) == 21 && word[2] == 8) { // OpTypeInteger 8-bit + if (word[3] > 0) { // signed + if (columnCount == 1 && componentCount <= 4 && componentCount != 3) { + field->type = SPV_I8 + (componentCount / 2); + } else { + return SPV_UNSUPPORTED_DATA_TYPE; + } + } else { + if (columnCount == 1 && componentCount <= 4 && componentCount != 3) { + field->type = SPV_U8 + (componentCount / 2); + } else { + return SPV_UNSUPPORTED_DATA_TYPE; + } + } + } else if (OP_CODE(word) == 21 && word[2] == 16) { // OpTypeInteger 16-bit + if (word[3] > 0) { // signed + if (columnCount == 1 && componentCount <= 4 && componentCount != 3) { + field->type = SPV_I16 + (componentCount / 2); + } else { + return SPV_UNSUPPORTED_DATA_TYPE; + } + } else { + if (columnCount == 1 && componentCount <= 4 && componentCount != 3) { + field->type = SPV_U16 + (componentCount / 2); + } else { + return SPV_UNSUPPORTED_DATA_TYPE; + } + } + } else if (OP_CODE(word) == 21 && word[2] == 32) { // OpTypeInteger 32-bit if (word[3] > 0) { // signed if (columnCount == 1 && componentCount >= 2 && componentCount <= 4) { field->type = SPV_I32x2 + componentCount - 2; @@ -767,7 +801,7 @@ static spv_result spv_parse_field(spv_context* spv, const uint32_t* word, spv_fi return SPV_UNSUPPORTED_DATA_TYPE; } - field->elementSize = 4 * componentCount * columnCount; + field->elementSize = (word[2] / 8) * componentCount * columnCount; return SPV_OK; } diff --git a/src/core/spv.h b/src/core/spv.h index ff129b173..8233a0a61 100644 --- a/src/core/spv.h +++ b/src/core/spv.h @@ -4,6 +4,21 @@ #pragma once typedef enum { + SPV_I8, + SPV_I8x2, + SPV_I8x4, + SPV_U8, + SPV_U8x2, + SPV_U8x4, + SPV_I16, + SPV_I16x2, + SPV_I16x4, + SPV_U16, + SPV_U16x2, + SPV_U16x4, + SPV_F16, + SPV_F16x2, + SPV_F16x4, SPV_B32, SPV_I32, SPV_I32x2, diff --git a/src/modules/graphics/graphics.c b/src/modules/graphics/graphics.c index 15bdf3a3a..1836b3d3b 100644 --- a/src/modules/graphics/graphics.c +++ b/src/modules/graphics/graphics.c @@ -991,8 +991,10 @@ void lovrGraphicsGetFeatures(GraphicsFeatures* features) { features->indirectDrawFirstInstance = state.features.indirectDrawFirstInstance; features->packedBuffers = state.features.packedBuffers; features->float64 = state.features.float64; + features->float16 = state.features.float16; features->int64 = state.features.int64; features->int16 = state.features.int16; + features->int8 = state.features.int8; } void lovrGraphicsGetLimits(GraphicsLimits* limits) { @@ -2094,9 +2096,17 @@ bool lovrGraphicsWait(void) { uint32_t lovrGraphicsAlignFields(DataField* parent, DataLayout layout) { static const struct { uint32_t size, scalarAlign, baseAlign; } table[] = { + [TYPE_I8] = { 1, 1, 1 }, + [TYPE_I8x2] = { 2, 1, 2 }, [TYPE_I8x4] = { 4, 1, 4 }, + [TYPE_U8] = { 1, 1, 1 }, + [TYPE_U8x2] = { 2, 1, 2 }, [TYPE_U8x4] = { 4, 1, 4 }, + [TYPE_SN8] = { 1, 1, 1 }, + [TYPE_SN8x2] = { 2, 1, 2 }, [TYPE_SN8x4] = { 4, 1, 4 }, + [TYPE_UN8] = { 1, 1, 1 }, + [TYPE_UN8x2] = { 2, 1, 2 }, [TYPE_UN8x4] = { 4, 1, 4 }, [TYPE_SN10x3] = { 4, 4, 4 }, [TYPE_UN10x3] = { 4, 4, 4 }, @@ -2106,8 +2116,10 @@ uint32_t lovrGraphicsAlignFields(DataField* parent, DataLayout layout) { [TYPE_U16] = { 2, 2, 2 }, [TYPE_U16x2] = { 4, 2, 4 }, [TYPE_U16x4] = { 8, 2, 8 }, + [TYPE_SN16] = { 2, 2, 2 }, [TYPE_SN16x2] = { 4, 2, 4 }, [TYPE_SN16x4] = { 8, 2, 8 }, + [TYPE_UN16] = { 2, 2, 2 }, [TYPE_UN16x2] = { 4, 2, 4 }, [TYPE_UN16x4] = { 8, 2, 8 }, [TYPE_I32] = { 4, 4, 4 }, @@ -2118,6 +2130,7 @@ uint32_t lovrGraphicsAlignFields(DataField* parent, DataLayout layout) { [TYPE_U32x2] = { 8, 4, 8 }, [TYPE_U32x3] = { 12, 4, 16 }, [TYPE_U32x4] = { 16, 4, 16 }, + [TYPE_F16] = { 2, 2, 2 }, [TYPE_F16x2] = { 4, 2, 4 }, [TYPE_F16x4] = { 8, 2, 8 }, [TYPE_F32] = { 4, 4, 4 }, @@ -3770,6 +3783,21 @@ Shader* lovrShaderCreate(const ShaderInfo* info) { for (uint32_t s = 0; s < info->stageCount; s++) { for (uint32_t i = 0; i < spv[s].fieldCount; i++) { static const DataType dataTypes[] = { + [SPV_I8] = TYPE_I8, + [SPV_I8x2] = TYPE_I8x2, + [SPV_I8x4] = TYPE_I8x4, + [SPV_U8] = TYPE_U8, + [SPV_U8x2] = TYPE_U8x2, + [SPV_U8x4] = TYPE_U8x4, + [SPV_I16] = TYPE_I16, + [SPV_I16x2] = TYPE_I16x2, + [SPV_I16x4] = TYPE_I16x4, + [SPV_U16] = TYPE_U16, + [SPV_U16x2] = TYPE_U16x2, + [SPV_U16x4] = TYPE_U16x4, + [SPV_F16] = TYPE_F16, + [SPV_F16x2] = TYPE_F16x2, + [SPV_F16x4] = TYPE_F16x4, [SPV_B32] = TYPE_U32, [SPV_I32] = TYPE_I32, [SPV_I32x2] = TYPE_I32x2, @@ -9833,7 +9861,7 @@ static bool checkShaderFeatures(uint32_t* features, uint32_t count) { case 2: return lovrSetError("Shader uses unsupported feature #%d: %s", features[i], "geometry shading"); case 3: return lovrSetError("Shader uses unsupported feature #%d: %s", features[i], "tessellation shading"); case 5: return lovrSetError("Shader uses unsupported feature #%d: %s", features[i], "linkage"); - case 9: return lovrSetError("Shader uses unsupported feature #%d: %s", features[i], "half floats"); + case 9: lovrCheck(state.features.float16, "GPU does not support shader feature #%d: %s", features[i], "16 bit floats"); break; case 10: lovrCheck(state.features.float64, "GPU does not support shader feature #%d: %s", features[i], "64 bit floats"); break; case 11: lovrCheck(state.features.int64, "GPU does not support shader feature #%d: %s", features[i], "64 bit integers"); break; case 12: return lovrSetError("Shader uses unsupported feature #%d: %s", features[i], "64 bit atomics"); @@ -9848,7 +9876,7 @@ static bool checkShaderFeatures(uint32_t* features, uint32_t count) { case 35: break; // SampleRateShading case 36: return lovrSetError("Shader uses unsupported feature #%d: %s", features[i], "rectangle textures"); case 37: return lovrSetError("Shader uses unsupported feature #%d: %s", features[i], "rectangle textures"); - case 39: return lovrSetError("Shader uses unsupported feature #%d: %s", features[i], "8 bit integers"); + case 39: lovrCheck(state.features.int8, "GPU does not support shader feature #%d: %s", features[i], "8 bit integers"); break; case 40: return lovrSetError("Shader uses unsupported feature #%d: %s", features[i], "input attachments"); case 41: return lovrSetError("Shader uses unsupported feature #%d: %s", features[i], "sparse residency"); case 42: return lovrSetError("Shader uses unsupported feature #%d: %s", features[i], "min LOD"); @@ -9878,8 +9906,13 @@ static bool checkShaderFeatures(uint32_t* features, uint32_t count) { case 69: return lovrSetError("Shader uses unsupported feature #%d: %s", features[i], "layered rendering"); case 70: return lovrSetError("Shader uses unsupported feature #%d: %s", features[i], "multiviewport"); case 4427: break; // ShaderDrawParameters + case 4433: lovrCheck(state.features.float16 || state.features.int16, "GPU does not support shader feature #%d: %s", features[i], "16 bit storage buffers"); break; + case 4434: return lovrSetError("Shader uses unsupported feature #%d: %s", features[i], "16 bit uniform/storage buffers"); + case 4436: return lovrSetError("Shader uses unsupported feature #%d: %s", features[i], "16 bit shader inputs/outputs"); case 4437: return lovrSetError("Shader uses unsupported feature #%d: %s", features[i], "multigpu"); case 4439: lovrCheck(state.limits.renderSize[2] > 1, "GPU does not support shader feature #%d: %s", features[i], "multiview"); break; + case 4448: lovrCheck(state.features.int8, "GPU does not support shader feature #%d: %s", features[i], "8 bit storage buffers"); break; + case 4449: return lovrSetError("Shader uses unsupported feature #%d: %s", features[i], "8 bit uniform/storage buffers"); case 4472: lovrCheck(state.features.rayQuery, "GPU does not support shader feature #%d: %s", features[i], "raytracing"); break; case 5301: return lovrSetError("Shader uses unsupported feature #%d: %s", features[i], "non-uniform indexing"); case 5306: return lovrSetError("Shader uses unsupported feature #%d: %s", features[i], "non-uniform indexing"); diff --git a/src/modules/graphics/graphics.h b/src/modules/graphics/graphics.h index 02011ddc9..b6172bd5f 100644 --- a/src/modules/graphics/graphics.h +++ b/src/modules/graphics/graphics.h @@ -52,8 +52,10 @@ typedef struct { bool indirectDrawFirstInstance; bool packedBuffers; bool float64; + bool float16; bool int64; bool int16; + bool int8; } GraphicsFeatures; typedef struct { @@ -131,9 +133,17 @@ bool lovrGraphicsWait(void); // Buffer typedef enum { + TYPE_I8, + TYPE_I8x2, TYPE_I8x4, + TYPE_U8, + TYPE_U8x2, TYPE_U8x4, + TYPE_SN8, + TYPE_SN8x2, TYPE_SN8x4, + TYPE_UN8, + TYPE_UN8x2, TYPE_UN8x4, TYPE_SN10x3, TYPE_UN10x3, @@ -143,8 +153,10 @@ typedef enum { TYPE_U16, TYPE_U16x2, TYPE_U16x4, + TYPE_SN16, TYPE_SN16x2, TYPE_SN16x4, + TYPE_UN16, TYPE_UN16x2, TYPE_UN16x4, TYPE_I32, @@ -155,6 +167,7 @@ typedef enum { TYPE_U32x2, TYPE_U32x3, TYPE_U32x4, + TYPE_F16, TYPE_F16x2, TYPE_F16x4, TYPE_F32,