From 4e113a3305e259199ae6f0f372b8d718e8705b96 Mon Sep 17 00:00:00 2001 From: luboslenco Date: Sat, 4 Apr 2026 13:07:01 +0200 Subject: [PATCH] base: bc7 support --- base/project.js | 1 - base/sources/backends/data/wasm/start.js | 10 +- base/sources/backends/direct3d12_gpu.c | 121 +++++++++++++++-------- base/sources/backends/metal_gpu.m | 96 ++++++++++++------ base/sources/backends/vulkan_gpu.c | 104 +++++-------------- base/sources/backends/webgpu.h | 1 + base/sources/backends/webgpu_gpu.c | 61 +++++++++--- base/sources/iron.h | 4 +- base/sources/iron_global.h | 4 - base/sources/iron_gpu.c | 95 ++++++++++++++++-- base/sources/iron_gpu.h | 8 +- base/tools/make.js | 1 + paint/project.js | 1 + paint/sources/tab_textures.c | 10 +- 14 files changed, 330 insertions(+), 187 deletions(-) diff --git a/base/project.js b/base/project.js index 4227a795..bf93c15f 100644 --- a/base/project.js +++ b/base/project.js @@ -208,7 +208,6 @@ if (flags.with_eval) { project.add_cfiles("sources/libs/minic_tests.c"); } -flags.with_bc7 = false; if (flags.with_bc7) { project.add_define("WITH_BC7"); project.add_cfiles("sources/libs/bc7enc.c"); diff --git a/base/sources/backends/data/wasm/start.js b/base/sources/backends/data/wasm/start.js index e423f95a..7eff582e 100644 --- a/base/sources/backends/data/wasm/start.js +++ b/base/sources/backends/data/wasm/start.js @@ -141,7 +141,10 @@ async function init() { throw new Error('WebGPU not supported'); } let adapter = await navigator.gpu.requestAdapter(); - let device = await adapter.requestDevice(); + let bc7_supported = adapter.features.has('texture-compression-bc'); + let device = await adapter.requestDevice({ + requiredFeatures: bc7_supported ? ['texture-compression-bc'] : [], + }); let canvas = document.getElementById('iron'); canvas.width = window.innerWidth; @@ -182,6 +185,11 @@ async function init() { wgpuAdapterRequestDeviceSync : function() { return ptr_to_id(device); }, + wgpuDeviceHasFeature : function(pdevice, feature) { + // WGPUFeatureName_TextureCompressionBC = 4 + if (feature === 4) { return bc7_supported ? 1 : 0; } + return 0; + }, wgpuDeviceCreateTexture : function(pdevice, pdescriptor) { let device = id_to_ptr(pdevice); // WGPUTextureDescriptor diff --git a/base/sources/backends/direct3d12_gpu.c b/base/sources/backends/direct3d12_gpu.c index baae53d5..ad0d86ae 100644 --- a/base/sources/backends/direct3d12_gpu.c +++ b/base/sources/backends/direct3d12_gpu.c @@ -265,8 +265,7 @@ void gpu_render_target_init2(gpu_texture_t *render_target, int width, int height .Texture2D.PlaneSlice = 0, }; - D3D12_CPU_DESCRIPTOR_HANDLE handle = - render_target->impl.is_dsv ? dsv_handle(render_target->impl.rtv_index) : rtv_handle(render_target->impl.rtv_index); + D3D12_CPU_DESCRIPTOR_HANDLE handle = render_target->impl.is_dsv ? dsv_handle(render_target->impl.rtv_index) : rtv_handle(render_target->impl.rtv_index); if (format == GPU_TEXTURE_FORMAT_D32) { device->lpVtbl->CreateDepthStencilView(device, render_target->impl.image, NULL, handle); @@ -295,12 +294,12 @@ void create_root_signature(bool linear_sampling) { ID3DBlob *error_blob; D3D12_ROOT_PARAMETER parameters[3] = {0}; D3D12_DESCRIPTOR_RANGE range = { - .RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV, - .NumDescriptors = (UINT)GPU_MAX_TEXTURES, - .BaseShaderRegister = 0, - .RegisterSpace = 0, - .OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND, - }; + .RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV, + .NumDescriptors = (UINT)GPU_MAX_TEXTURES, + .BaseShaderRegister = 0, + .RegisterSpace = 0, + .OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND, + }; parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; parameters[0].DescriptorTable.NumDescriptorRanges = 1; @@ -310,12 +309,12 @@ void create_root_signature(bool linear_sampling) { parameters[1].Descriptor.ShaderRegister = 0; parameters[1].Descriptor.RegisterSpace = 0; D3D12_DESCRIPTOR_RANGE sampler_range = { - .RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, - .NumDescriptors = 1, - .BaseShaderRegister = 0, - .RegisterSpace = 0, - .OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND, - }; + .RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, + .NumDescriptors = 1, + .BaseShaderRegister = 0, + .RegisterSpace = 0, + .OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND, + }; parameters[2].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; parameters[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; parameters[2].DescriptorTable.NumDescriptorRanges = 1; @@ -444,8 +443,7 @@ void gpu_begin_internal(gpu_clear_t flags, unsigned color, float depth) { command_list->lpVtbl->ClearRenderTargetView(command_list, rtv_handle(target->impl.rtv_index), clear_color, 0, NULL); } if (flags & GPU_CLEAR_DEPTH && current_depth_buffer != NULL) { - command_list->lpVtbl->ClearDepthStencilView(command_list, dsv_handle(current_depth_buffer->impl.rtv_index), D3D12_CLEAR_FLAG_DEPTH, depth, 0, 0, - NULL); + command_list->lpVtbl->ClearDepthStencilView(command_list, dsv_handle(current_depth_buffer->impl.rtv_index), D3D12_CLEAR_FLAG_DEPTH, depth, 0, 0, NULL); } } @@ -550,8 +548,7 @@ void gpu_internal_set_textures() { for (int i = 0; i < GPU_MAX_TEXTURES; ++i) { if (current_textures[i] != NULL) { - device->lpVtbl->CopyDescriptorsSimple(device, 1, cpu_base, srv_handle(current_textures[i]->impl.srv_index), - D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + device->lpVtbl->CopyDescriptorsSimple(device, 1, cpu_base, srv_handle(current_textures[i]->impl.srv_index), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); cpu_base.ptr += srv_step; gpu_srv_heap_index++; } @@ -833,8 +830,8 @@ void gpu_pipeline_compile(gpu_pipeline_t *pipe) { psoDesc.BlendState.IndependentBlendEnable = true; for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i) { - psoDesc.BlendState.RenderTarget[i].BlendEnable = pipe->blend_source != GPU_BLEND_ONE || pipe->blend_destination != GPU_BLEND_ZERO || - pipe->alpha_blend_source != GPU_BLEND_ONE || pipe->alpha_blend_destination != GPU_BLEND_ZERO; + psoDesc.BlendState.RenderTarget[i].BlendEnable = pipe->blend_source != GPU_BLEND_ONE || pipe->blend_destination != GPU_BLEND_ZERO || + pipe->alpha_blend_source != GPU_BLEND_ONE || pipe->alpha_blend_destination != GPU_BLEND_ZERO; psoDesc.BlendState.RenderTarget[i].SrcBlend = convert_blend_factor(pipe->blend_source); psoDesc.BlendState.RenderTarget[i].DestBlend = convert_blend_factor(pipe->blend_destination); psoDesc.BlendState.RenderTarget[i].BlendOp = D3D12_BLEND_OP_ADD; @@ -862,14 +859,23 @@ void gpu_shader_destroy(gpu_shader_t *shader) { } void gpu_texture_init_from_bytes(gpu_texture_t *texture, void *data, int width, int height, gpu_texture_format_t format) { - texture->width = width; - texture->height = height; - texture->format = format; - texture->state = GPU_TEXTURE_STATE_SHADER_RESOURCE; - texture->buffer = NULL; - texture->impl.rtv_index = -1; - DXGI_FORMAT dxgi_format = convert_format(format); - int format_size = gpu_texture_format_size(format); + texture->width = width; + texture->height = height; + texture->format = format; + texture->state = GPU_TEXTURE_STATE_SHADER_RESOURCE; + texture->buffer = NULL; + texture->impl.rtv_index = -1; + DXGI_FORMAT dxgi_format = convert_format(format); + int format_size = gpu_texture_format_size(format); + void *original_data = data; + +#ifdef WITH_BC7 + if (gpu_bc7_supported(width, height, format)) { + texture->format = GPU_TEXTURE_FORMAT_RGBA32_BC7; + dxgi_format = DXGI_FORMAT_BC7_UNORM; + data = gpu_bc7_compress(data, width, height); + } +#endif D3D12_HEAP_PROPERTIES heap_properties = { .Type = D3D12_HEAP_TYPE_DEFAULT, @@ -899,8 +905,13 @@ void gpu_texture_init_from_bytes(gpu_texture_t *texture, void *data, int width, if (result != S_OK && gpu_cleanup_pending()) { gpu_execute_and_wait(); gpu_cleanup(); - device->lpVtbl->CreateCommittedResource(device, &heap_properties, D3D12_HEAP_FLAG_NONE, &resource_desc, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, - NULL, &IID_ID3D12Resource, &texture->impl.image); +#ifdef WITH_BC7 + if (data != original_data) { + free(data); + } +#endif + gpu_texture_init_from_bytes(texture, original_data, width, height, format); + return; } D3D12_PLACED_SUBRESOURCE_FOOTPRINT footprint; @@ -946,8 +957,16 @@ void gpu_texture_init_from_bytes(gpu_texture_t *texture, void *data, int width, BYTE *pixel; upload_buffer->lpVtbl->Map(upload_buffer, 0, NULL, (void **)&pixel); UINT row_pitch = footprint.Footprint.RowPitch; - for (int y = 0; y < texture->height; ++y) { - memcpy(pixel + y * row_pitch, ((uint8_t *)data) + y * width * format_size, width * format_size); +#ifdef WITH_BC7 + if (data != original_data) { + memcpy(pixel, data, ((width + 3) / 4) * ((height + 3) / 4) * 16); // BC7ENC_BLOCK_SIZE + } + else +#endif + { + for (int y = 0; y < texture->height; ++y) { + memcpy(pixel + y * row_pitch, ((uint8_t *)data) + y * width * format_size, width * format_size); + } } upload_buffer->lpVtbl->Unmap(upload_buffer, 0, NULL); @@ -991,6 +1010,12 @@ void gpu_texture_init_from_bytes(gpu_texture_t *texture, void *data, int width, command_list->lpVtbl->ResourceBarrier(command_list, 1, &barrier); gpu_execute_and_wait(); //// + +#ifdef WITH_BC7 + if (data != original_data) { + free(data); + } +#endif } void gpu_texture_destroy_internal(gpu_texture_t *tex) { @@ -1136,9 +1161,9 @@ void gpu_constant_buffer_lock(gpu_buffer_t *buffer, int start, int count) { buffer->impl.last_start = start; buffer->impl.last_count = count; D3D12_RANGE range = { - .Begin = start, - .End = start + count, - }; + .Begin = start, + .End = start + count, + }; uint8_t *p; buffer->impl.buffer->lpVtbl->Map(buffer->impl.buffer, 0, &range, (void **)&p); buffer->data = &p[start]; @@ -1171,6 +1196,20 @@ char *gpu_device_name() { return device_name; } +bool gpu_bc7_supported(int width, int height, gpu_texture_format_t format) { + static bool bc7_supported = false; +#ifdef WITH_BC7 + static bool bc7_checked = false; + if (!bc7_checked) { + bc7_checked = true; + D3D12_FEATURE_DATA_FORMAT_SUPPORT fmt = {.Format = DXGI_FORMAT_BC7_UNORM}; + bc7_supported = SUCCEEDED(device->lpVtbl->CheckFeatureSupport(device, D3D12_FEATURE_FORMAT_SUPPORT, &fmt, sizeof(fmt))); + } +#endif + return bc7_supported && format == GPU_TEXTURE_FORMAT_RGBA32 && width >= 2048 && height >= 2048 && (width & (width - 1)) == 0 && + (height & (height - 1)) == 0; +} + typedef struct inst { mat4_t m; int i; @@ -1478,12 +1517,12 @@ void gpu_raytrace_acceleration_structure_build(gpu_acceleration_structure_t *acc D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO bottom_level_prebuild_info = {0}; D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS inputs = { - .DescsLayout = D3D12_ELEMENTS_LAYOUT_ARRAY, - .NumDescs = 1, - .Type = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL, - .pGeometryDescs = &geometry_descs[i], - .Flags = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAG_PREFER_FAST_TRACE, - }; + .DescsLayout = D3D12_ELEMENTS_LAYOUT_ARRAY, + .NumDescs = 1, + .Type = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL, + .pGeometryDescs = &geometry_descs[i], + .Flags = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAG_PREFER_FAST_TRACE, + }; dxr_device->lpVtbl->GetRaytracingAccelerationStructurePrebuildInfo(dxr_device, &inputs, &bottom_level_prebuild_info); bottom_level_inputs[i] = inputs; diff --git a/base/sources/backends/metal_gpu.m b/base/sources/backends/metal_gpu.m index 7798b548..bfb7cadb 100644 --- a/base/sources/backends/metal_gpu.m +++ b/base/sources/backends/metal_gpu.m @@ -378,7 +378,7 @@ void gpu_get_render_target_pixels(gpu_texture_t *render_target, uint8_t *data) { // Read buffer id buffer = (__bridge id)readback_buffer; - memcpy(data, [buffer contents], render_target -> width * render_target -> height *gpu_texture_format_size(render_target->format)); + memcpy(data, [buffer contents], render_target->width * render_target->height * gpu_texture_format_size(render_target->format)); } void gpu_set_constant_buffer(gpu_buffer_t *buffer, int offset, size_t size) { @@ -543,6 +543,21 @@ void gpu_texture_init_from_bytes(gpu_texture_t *texture, void *data, int width, if (mtlformat == MTLPixelFormatBGRA8Unorm) { mtlformat = MTLPixelFormatRGBA8Unorm; } + + void *original_data = data; + int bytes_per_row = width * gpu_texture_format_size(format); + int bytes_per_image = bytes_per_row * height; + +#ifdef WITH_BC7 + if (gpu_bc7_supported(width, height, format)) { + texture->format = GPU_TEXTURE_FORMAT_RGBA32_BC7; + mtlformat = MTLPixelFormatBC7_RGBAUnorm; + data = gpu_bc7_compress(data, width, height); + bytes_per_row = ((width + 3) / 4) * 16; // BC7ENC_BLOCK_SIZE + bytes_per_image = bytes_per_row * ((height + 3) / 4); + } +#endif + MTLTextureDescriptor *descriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:mtlformat width:width height:height mipmapped:NO]; descriptor.textureType = MTLTextureType2D; descriptor.width = width; @@ -557,15 +572,22 @@ void gpu_texture_init_from_bytes(gpu_texture_t *texture, void *data, int width, id tex = [device newTextureWithDescriptor:descriptor]; if (tex == nil) { gpu_cleanup(); - tex = [device newTextureWithDescriptor:descriptor]; +#ifdef WITH_BC7 + if (data != original_data) { + free(data); + } +#endif + gpu_texture_init_from_bytes(texture, original_data, width, height, format); + return; } - texture->impl._tex = (__bridge_retained void *)tex; - [tex replaceRegion:MTLRegionMake2D(0, 0, width, height) - mipmapLevel:0 - slice:0 - withBytes:data - bytesPerRow:width * gpu_texture_format_size(format) - bytesPerImage:width * gpu_texture_format_size(format) * height]; + texture->impl._tex = (__bridge_retained void *)tex; + [tex replaceRegion:MTLRegionMake2D(0, 0, width, height) mipmapLevel:0 slice:0 withBytes:data bytesPerRow:bytes_per_row bytesPerImage:bytes_per_image]; + +#ifdef WITH_BC7 + if (data != original_data) { + free(data); + } +#endif } void gpu_texture_destroy_internal(gpu_texture_t *target) { @@ -651,33 +673,43 @@ char *gpu_device_name() { return (char *)[device.name UTF8String]; } +bool gpu_bc7_supported(int width, int height, gpu_texture_format_t format) { +#ifdef WITH_BC7 + id device = get_metal_device(); + return [device supportsBCTextureCompression] && format == GPU_TEXTURE_FORMAT_RGBA32 && width >= 2048 && height >= 2048 && (width & (width - 1)) == 0 && + (height & (height - 1)) == 0; +#else + return false; +#endif +} + typedef struct inst { mat4_t m; - int i; + int i; } inst_t; -static gpu_acceleration_structure_t *accel; -static gpu_raytrace_pipeline_t *pipeline; -static gpu_texture_t *output = NULL; -static gpu_buffer_t *constant_buf; -static id _raytracing_pipeline; -static NSMutableArray *_primitive_accels; -static id _instance_accel; -static dispatch_semaphore_t _semaphore; -static gpu_texture_t *_texpaint0; -static gpu_texture_t *_texpaint1; -static gpu_texture_t *_texpaint2; -static gpu_texture_t *_texenv; -static gpu_texture_t *_texsobol; -static gpu_texture_t *_texscramble; -static gpu_texture_t *_texrank; -static gpu_buffer_t *vb[16]; -static gpu_buffer_t *vb_last[16]; -static gpu_buffer_t *ib[16]; -static int vb_count = 0; -static int vb_count_last = 0; -static inst_t instances[1024]; -static int instances_count = 0; +static gpu_acceleration_structure_t *accel; +static gpu_raytrace_pipeline_t *pipeline; +static gpu_texture_t *output = NULL; +static gpu_buffer_t *constant_buf; +static id _raytracing_pipeline; +static NSMutableArray *_primitive_accels; +static id _instance_accel; +static dispatch_semaphore_t _semaphore; +static gpu_texture_t *_texpaint0; +static gpu_texture_t *_texpaint1; +static gpu_texture_t *_texpaint2; +static gpu_texture_t *_texenv; +static gpu_texture_t *_texsobol; +static gpu_texture_t *_texscramble; +static gpu_texture_t *_texrank; +static gpu_buffer_t *vb[16]; +static gpu_buffer_t *vb_last[16]; +static gpu_buffer_t *ib[16]; +static int vb_count = 0; +static int vb_count_last = 0; +static inst_t instances[1024]; +static int instances_count = 0; void gpu_raytrace_pipeline_init(gpu_raytrace_pipeline_t *pipeline, void *shader, int ray_shader_size, gpu_buffer_t *constant_buffer) { id device = get_metal_device(); diff --git a/base/sources/backends/vulkan_gpu.c b/base/sources/backends/vulkan_gpu.c index b9ad21d4..7edba6df 100644 --- a/base/sources/backends/vulkan_gpu.c +++ b/base/sources/backends/vulkan_gpu.c @@ -1613,48 +1613,6 @@ void gpu_shader_destroy(gpu_shader_t *shader) { shader->impl.source = NULL; } -#ifdef WITH_BC7 -#include -#include -#define BC7_THREAD_COUNT 16 - -typedef struct { - uint8_t *src; - uint8_t *dst; - int width; - int height; - int blocks_x; - int total_blocks; - volatile int32_t *next_block; - bc7enc_compress_block_params params; -} bc7_thread_params_t; - -static void bc7_thread_func(void *arg) { - bc7_thread_params_t *p = (bc7_thread_params_t *)arg; - for (;;) { - int bi = iron_atomic_increment(p->next_block); - if (bi >= p->total_blocks) - break; - int bx = bi % p->blocks_x; - int by = bi / p->blocks_x; - uint8_t block[64]; - for (int py = 0; py < 4; py++) { - for (int px = 0; px < 4; px++) { - int sx = bx * 4 + px < p->width ? bx * 4 + px : p->width - 1; - int sy = by * 4 + py < p->height ? by * 4 + py : p->height - 1; - int src_idx = (sy * p->width + sx) * 4; - int dst_idx = (py * 4 + px) * 4; - block[dst_idx + 0] = p->src[src_idx + 0]; - block[dst_idx + 1] = p->src[src_idx + 1]; - block[dst_idx + 2] = p->src[src_idx + 2]; - block[dst_idx + 3] = p->src[src_idx + 3]; - } - } - bc7enc_compress_block(p->dst + (size_t)bi * BC7ENC_BLOCK_SIZE, block, &p->params); - } -} -#endif - void gpu_texture_init_from_bytes(gpu_texture_t *texture, void *data, int width, int height, gpu_texture_format_t format) { texture->width = width; texture->height = height; @@ -1671,40 +1629,11 @@ void gpu_texture_init_from_bytes(gpu_texture_t *texture, void *data, int width, void *original_data = data; #ifdef WITH_BC7 - void *bc7_data = NULL; - if (format == GPU_TEXTURE_FORMAT_RGBA32 && width >= 2048 && height >= 2048) { - vk_format = VK_FORMAT_BC7_UNORM_BLOCK; - int blocks_x = (width + 3) / 4; - int blocks_y = (height + 3) / 4; - bc7_data = malloc(blocks_x * blocks_y * BC7ENC_BLOCK_SIZE); - static bc7enc_bool bc7enc_ready = BC7ENC_FALSE; - if (!bc7enc_ready) { - bc7enc_compress_block_init(); - bc7enc_ready = BC7ENC_TRUE; - } - volatile int32_t next_block = 0; - bc7_thread_params_t tp = { - .src = (uint8_t *)data, - .dst = (uint8_t *)bc7_data, - .width = width, - .height = height, - .blocks_x = blocks_x, - .total_blocks = blocks_x * blocks_y, - .next_block = &next_block, - }; - bc7enc_compress_block_params_init(&tp.params); - tp.params.m_max_partitions_mode = 0; - tp.params.m_try_least_squares = false; - - iron_thread_t threads[BC7_THREAD_COUNT]; - for (int i = 0; i < BC7_THREAD_COUNT; i++) { - iron_thread_init(&threads[i], bc7_thread_func, &tp); - } - for (int i = 0; i < BC7_THREAD_COUNT; i++) { - iron_thread_wait_and_destroy(&threads[i]); - } - data = bc7_data; - _upload_size = (VkDeviceSize)((width + 3) / 4) * ((height + 3) / 4) * BC7ENC_BLOCK_SIZE; + if (gpu_bc7_supported(width, height, format)) { + texture->format = GPU_TEXTURE_FORMAT_RGBA32_BC7; + vk_format = VK_FORMAT_BC7_UNORM_BLOCK; + data = gpu_bc7_compress(data, width, height); + _upload_size = (VkDeviceSize)((width + 3) / 4) * ((height + 3) / 4) * 16; // BC7ENC_BLOCK_SIZE } #endif @@ -1773,7 +1702,9 @@ void gpu_texture_init_from_bytes(gpu_texture_t *texture, void *data, int width, gpu_cleanup_internal(); gpu_cleanup(); #ifdef WITH_BC7 - free(bc7_data); + if (data != original_data) { + free(data); + } #endif gpu_texture_init_from_bytes(texture, original_data, width, height, format); return; @@ -1846,7 +1777,9 @@ void gpu_texture_init_from_bytes(gpu_texture_t *texture, void *data, int width, gpu_execute_and_wait(); //// #ifdef WITH_BC7 - free(bc7_data); + if (data != original_data) { + free(data); + } #endif } @@ -1999,6 +1932,21 @@ char *gpu_device_name() { return device_name; } +bool gpu_bc7_supported(int width, int height, gpu_texture_format_t format) { + static bool bc7_supported = false; +#ifdef WITH_BC7 + static bool bc7_checked = false; + if (!bc7_checked) { + bc7_checked = true; + VkFormatProperties props; + vkGetPhysicalDeviceFormatProperties(gpu, VK_FORMAT_BC7_UNORM_BLOCK, &props); + bc7_supported = (props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) != 0; + } +#endif + return bc7_supported && format == GPU_TEXTURE_FORMAT_RGBA32 && width >= 2048 && height >= 2048 && (width & (width - 1)) == 0 && + (height & (height - 1)) == 0; +} + typedef struct inst { mat4_t m; int i; diff --git a/base/sources/backends/webgpu.h b/base/sources/backends/webgpu.h index 6a69e810..672c8c38 100644 --- a/base/sources/backends/webgpu.h +++ b/base/sources/backends/webgpu.h @@ -1212,4 +1212,5 @@ IMPORT("wgpuSurfaceConfigure") void wgpuSurfaceConfigure(WGPUSurface surface, WG IMPORT("wgpuInstanceRequestAdapterSync") WGPUAdapter wgpuInstanceRequestAdapterSync(); IMPORT("wgpuAdapterRequestDeviceSync") WGPUDevice wgpuAdapterRequestDeviceSync(); +IMPORT("wgpuDeviceHasFeature") WGPUBool wgpuDeviceHasFeature(WGPUDevice device, WGPUFeatureName feature); IMPORT("wgpuBufferUnmap2") void wgpuBufferUnmap2(WGPUBuffer buffer, void *data, int start, int count); diff --git a/base/sources/backends/webgpu_gpu.c b/base/sources/backends/webgpu_gpu.c index 8425db63..b0372140 100644 --- a/base/sources/backends/webgpu_gpu.c +++ b/base/sources/backends/webgpu_gpu.c @@ -135,9 +135,9 @@ static void create_descriptors(void) { }; descriptor_layout = wgpuDeviceCreateBindGroupLayout(device, &layout_create_info); - bindings[1].sampler.type = WGPUSamplerBindingType_NonFiltering; + bindings[1].sampler.type = WGPUSamplerBindingType_NonFiltering; bindings[2].texture.sampleType = WGPUTextureSampleType_UnfilterableFloat; - descriptor_layout_depth = wgpuDeviceCreateBindGroupLayout(device, &layout_create_info); + descriptor_layout_depth = wgpuDeviceCreateBindGroupLayout(device, &layout_create_info); WGPUTextureDescriptor dummy_desc = { .size = {1, 1, 1}, @@ -318,10 +318,10 @@ void gpu_begin_internal(gpu_clear_t flags, unsigned color, float depth) { wgpuSurfaceGetCurrentTexture(surface, &surface_texture); framebuffers[0].impl.texture = surface_texture.texture; WGPUTextureViewDescriptor view_info = { - .dimension = WGPUTextureViewDimension_2D, - .format = WGPUTextureFormat_RGBA8Unorm, - .mipLevelCount = 1, - .arrayLayerCount = 1, + .dimension = WGPUTextureViewDimension_2D, + .format = WGPUTextureFormat_RGBA8Unorm, + .mipLevelCount = 1, + .arrayLayerCount = 1, }; framebuffers[0].impl.view = wgpuTextureCreateView(surface_texture.texture, &view_info); framebuffers[0].width = width; @@ -531,7 +531,7 @@ void gpu_pipeline_compile(gpu_pipeline_t *pipeline) { pipeline->impl.pipeline_layout = wgpuDeviceCreatePipelineLayout(device, &pipeline_layout_create_info); pipeline_layout_create_info.bindGroupLayouts = &descriptor_layout_depth; - pipeline->impl.pipeline_layout_depth = wgpuDeviceCreatePipelineLayout(device, &pipeline_layout_create_info); + pipeline->impl.pipeline_layout_depth = wgpuDeviceCreatePipelineLayout(device, &pipeline_layout_create_info); WGPURenderPipelineDescriptor pipeline_desc = {0}; pipeline_desc.layout = pipeline->impl.pipeline_layout; @@ -625,7 +625,7 @@ void gpu_pipeline_compile(gpu_pipeline_t *pipeline) { pipeline->impl.pipeline = wgpuDeviceCreateRenderPipeline(device, &pipeline_desc); - pipeline_desc.layout = pipeline->impl.pipeline_layout_depth; + pipeline_desc.layout = pipeline->impl.pipeline_layout_depth; pipeline->impl.pipeline_depth = wgpuDeviceCreateRenderPipeline(device, &pipeline_desc); wgpuShaderModuleRelease(pipeline_desc.vertex.module); @@ -650,12 +650,29 @@ void gpu_texture_init_from_bytes(gpu_texture_t *texture, void *data, int width, texture->format = format; texture->state = GPU_TEXTURE_STATE_SHADER_RESOURCE; - WGPUTextureFormat wgpu_format = convert_image_format(format); - int bpp = gpu_texture_format_size(format); - int aligned_bpr = bytes_per_row_align(width * bpp); - size_t upload_size = width * height * bpp; - void *upload_data = data; + WGPUTextureFormat wgpu_format = convert_image_format(format); + int bpp = gpu_texture_format_size(format); + void *original_data = data; +#ifdef WITH_BC7 + if (gpu_bc7_supported(width, height, format)) { + texture->format = GPU_TEXTURE_FORMAT_RGBA32_BC7; + wgpu_format = WGPUTextureFormat_BC7RGBAUnorm; + data = gpu_bc7_compress(data, width, height); + } +#endif + + int aligned_bpr = bytes_per_row_align(width * bpp); + size_t upload_size = width * height * bpp; + void *upload_data = data; + +#ifdef WITH_BC7 + if (data != original_data) { + aligned_bpr = ((width + 3) / 4) * 16; // BC7ENC_BLOCK_SIZE + upload_size = (size_t)aligned_bpr * ((height + 3) / 4); + } + else +#endif if (aligned_bpr != width * bpp) { upload_size = (size_t)aligned_bpr * height; upload_data = malloc(upload_size); @@ -685,6 +702,11 @@ void gpu_texture_init_from_bytes(gpu_texture_t *texture, void *data, int width, if (upload_data != data) { free(upload_data); } +#ifdef WITH_BC7 + if (data != original_data) { + free(data); + } +#endif WGPUTextureDescriptor image_info = { .size = {(uint32_t)width, (uint32_t)height, 1}, @@ -800,7 +822,18 @@ char *gpu_device_name() { return device_name; } -bool gpu_raytrace_supported(void) { return false; } +bool gpu_bc7_supported(int width, int height, gpu_texture_format_t format) { + bool bc7_supported = false; +#ifdef WITH_BC7 + bc7_supported = wgpuDeviceHasFeature(device, WGPUFeatureName_TextureCompressionBC); +#endif + return bc7_supported && format == GPU_TEXTURE_FORMAT_RGBA32 && width >= 2048 && height >= 2048 && (width & (width - 1)) == 0 && + (height & (height - 1)) == 0; +} + +bool gpu_raytrace_supported(void) { + return false; +} void gpu_raytrace_pipeline_init(gpu_raytrace_pipeline_t *pipeline, void *shader, int shader_size, gpu_buffer_t *constant_buffer) {} void gpu_raytrace_pipeline_destroy(gpu_raytrace_pipeline_t *pipeline) {} void gpu_raytrace_acceleration_structure_init(gpu_acceleration_structure_t *accel) {} diff --git a/base/sources/iron.h b/base/sources/iron.h index 0b8f4a99..02ff8264 100644 --- a/base/sources/iron.h +++ b/base/sources/iron.h @@ -871,11 +871,9 @@ gpu_texture_t *gpu_create_texture_from_encoded_bytes(buffer_t *data, char *forma } // double t = iron_time(); //// - gpu_texture_init_from_bytes(texture, texture_data, width, height, texture_format); - free(texture_data); - // iron_log("gpu_texture_init_from_bytes in %fs\n", iron_time() - t); //// + free(texture_data); return texture; } diff --git a/base/sources/iron_global.h b/base/sources/iron_global.h index 1829c10c..3d7a38da 100644 --- a/base/sources/iron_global.h +++ b/base/sources/iron_global.h @@ -9,12 +9,8 @@ #include #if TARGET_OS_IPHONE #define IRON_IOS -#define IRON_APPLE_SOC #else #define IRON_MACOS -#if defined(__arm64__) -#define IRON_APPLE_SOC -#endif #endif #define IRON_POSIX #elif defined(__linux__) diff --git a/base/sources/iron_gpu.c b/base/sources/iron_gpu.c index bebf9251..24ca6b28 100644 --- a/base/sources/iron_gpu.c +++ b/base/sources/iron_gpu.c @@ -1,6 +1,6 @@ #include "iron_gpu.h" -#include "iron_system.h" #include "iron_math.h" +#include "iron_system.h" #include static gpu_buffer_t constant_buffer; @@ -255,8 +255,8 @@ void gpu_vertex_structure_add(gpu_vertex_structure_t *structure, const char *nam void gpu_vertex_struct_add(gpu_vertex_structure_t *raw, char *name, gpu_vertex_data_t data) { gpu_vertex_element_t *e = &raw->elements[raw->size]; // e->name = string_copy(name); - e->name = name; - e->data = data; + e->name = name; + e->data = data; raw->size++; } @@ -373,12 +373,12 @@ int gpu_texture_format_size(gpu_texture_format_t format) { } } -static gpu_buffer_t rt_constant_buffer; -static gpu_raytrace_pipeline_t rt_pipeline; -static gpu_acceleration_structure_t rt_accel; -static bool rt_created = false; -static bool rt_accel_created = false; -static const int rt_constant_buffer_size = 24; +static gpu_buffer_t rt_constant_buffer; +static gpu_raytrace_pipeline_t rt_pipeline; +static gpu_acceleration_structure_t rt_accel; +static bool rt_created = false; +static bool rt_accel_created = false; +static const int rt_constant_buffer_size = 24; void _gpu_raytrace_init(buffer_t *shader) { if (rt_created) { @@ -420,3 +420,80 @@ void _gpu_raytrace_dispatch_rays(gpu_texture_t *render_target, buffer_t *buffer) gpu_raytrace_set_target(render_target); gpu_raytrace_dispatch_rays(); } + +#ifdef WITH_BC7 + +#include +#include +#define BC7_THREAD_COUNT 16 + +typedef struct { + uint8_t *src; + uint8_t *dst; + int width; + int height; + int blocks_x; + int total_blocks; + volatile int32_t *next_block; + bc7enc_compress_block_params params; +} bc7_thread_params_t; + +static void bc7_thread_func(void *arg) { + bc7_thread_params_t *p = (bc7_thread_params_t *)arg; + for (;;) { + int bi = iron_atomic_increment(p->next_block); + if (bi >= p->total_blocks) + break; + int bx = bi % p->blocks_x; + int by = bi / p->blocks_x; + uint8_t block[64]; + for (int py = 0; py < 4; py++) { + for (int px = 0; px < 4; px++) { + int sx = bx * 4 + px < p->width ? bx * 4 + px : p->width - 1; + int sy = by * 4 + py < p->height ? by * 4 + py : p->height - 1; + int src_idx = (sy * p->width + sx) * 4; + int dst_idx = (py * 4 + px) * 4; + block[dst_idx + 0] = p->src[src_idx + 0]; + block[dst_idx + 1] = p->src[src_idx + 1]; + block[dst_idx + 2] = p->src[src_idx + 2]; + block[dst_idx + 3] = p->src[src_idx + 3]; + } + } + bc7enc_compress_block(p->dst + (size_t)bi * BC7ENC_BLOCK_SIZE, block, &p->params); + } +} + +void *gpu_bc7_compress(void *data, int width, int height) { + int blocks_x = (width + 3) / 4; + int blocks_y = (height + 3) / 4; + void *bc7_data = malloc(blocks_x * blocks_y * BC7ENC_BLOCK_SIZE); + static bc7enc_bool bc7enc_ready = BC7ENC_FALSE; + if (!bc7enc_ready) { + bc7enc_compress_block_init(); + bc7enc_ready = BC7ENC_TRUE; + } + volatile int32_t next_block = 0; + bc7_thread_params_t tp = { + .src = (uint8_t *)data, + .dst = (uint8_t *)bc7_data, + .width = width, + .height = height, + .blocks_x = blocks_x, + .total_blocks = blocks_x * blocks_y, + .next_block = &next_block, + }; + bc7enc_compress_block_params_init(&tp.params); + tp.params.m_max_partitions_mode = 0; + tp.params.m_try_least_squares = false; + + iron_thread_t threads[BC7_THREAD_COUNT]; + for (int i = 0; i < BC7_THREAD_COUNT; i++) { + iron_thread_init(&threads[i], bc7_thread_func, &tp); + } + for (int i = 0; i < BC7_THREAD_COUNT; i++) { + iron_thread_wait_and_destroy(&threads[i]); + } + return bc7_data; +} + +#endif diff --git a/base/sources/iron_gpu.h b/base/sources/iron_gpu.h index 65a247a1..394ec968 100644 --- a/base/sources/iron_gpu.h +++ b/base/sources/iron_gpu.h @@ -55,7 +55,8 @@ typedef enum { GPU_TEXTURE_FORMAT_R8, GPU_TEXTURE_FORMAT_R16, GPU_TEXTURE_FORMAT_R32, - GPU_TEXTURE_FORMAT_D32 + GPU_TEXTURE_FORMAT_D32, + GPU_TEXTURE_FORMAT_RGBA32_BC7 } gpu_texture_format_t; typedef enum { @@ -226,6 +227,11 @@ void gpu_get_render_target_pixels(gpu_texture_t *render_target, uint8_t *data); void gpu_set_texture(int unit, gpu_texture_t *texture); void gpu_use_linear_sampling(bool b); char *gpu_device_name(); +bool gpu_bc7_supported(int width, int height, gpu_texture_format_t format); + +#ifdef WITH_BC7 +void *gpu_bc7_compress(void *data, int width, int height); +#endif bool gpu_raytrace_supported(void); void gpu_raytrace_pipeline_init(gpu_raytrace_pipeline_t *pipeline, void *shader, int shader_size, gpu_buffer_t *constant_buffer); diff --git a/base/tools/make.js b/base/tools/make.js index c21a2077..d4325fac 100644 --- a/base/tools/make.js +++ b/base/tools/make.js @@ -2348,6 +2348,7 @@ function load_project(directory, is_root_project) { with_plugins : false, with_kong : false, with_raytrace : false, + with_bc7 : false, idle_sleep : false, export_version_info : false, export_data_list : false diff --git a/paint/project.js b/paint/project.js index 6bc24811..e50b456c 100644 --- a/paint/project.js +++ b/paint/project.js @@ -13,6 +13,7 @@ flags.with_eval = true; flags.with_plugins = true; flags.with_kong = true; flags.with_raytrace = true; +flags.with_bc7 = true; flags.idle_sleep = true; flags.export_version_info = true; flags.export_data_list = platform == "android"; // .apk contents diff --git a/paint/sources/tab_textures.c b/paint/sources/tab_textures.c index c2503132..efd4ec21 100644 --- a/paint/sources/tab_textures.c +++ b/paint/sources/tab_textures.c @@ -180,12 +180,16 @@ void tab_textures_draw(ui_handle_t *htab) { if (ui->is_hovered) { ui_tooltip_image(img, 256); + char *tooltip = asset->name; if (is_packed) { - ui_tooltip(string("%s %s", asset->name, tr("(packed)"))); + tooltip = string("%s %s", tooltip, tr("(packed)")); } - else { - ui_tooltip(asset->name); + #ifdef WITH_BC7 + if (img->format == GPU_TEXTURE_FORMAT_RGBA32_BC7) { + tooltip = string("%s %s", tooltip, tr("(compressed)")); } + #endif + ui_tooltip(tooltip); } if (ui->is_hovered && ui->input_released_r) {