From 82b0a94cfc5e56317e31bd6fbeb1a18898b80dd9 Mon Sep 17 00:00:00 2001 From: Andre Weissflog Date: Sat, 24 Oct 2026 13:32:31 +0200 Subject: [PATCH] update and fix samples for sg_image_desc.data type change --- examples/sgl/sgl.rs | 18 +- examples/texcube/texcube.rs | 4 +- src/gfx.rs | 142 +++-- src/sokol/c/sokol_debugtext.h | 2 +- src/sokol/c/sokol_gfx.h | 1032 +++++++++++++++++++++++++-------- src/sokol/c/sokol_gfx_imgui.h | 12 +- src/sokol/c/sokol_gl.h | 2 +- 7 files changed, 896 insertions(+), 316 deletions(-) diff --git a/examples/sgl/sgl.rs b/examples/sgl/sgl.rs index 2b9ba54..d75644f 100644 --- a/examples/sgl/sgl.rs +++ b/examples/sgl/sgl.rs @@ -47,20 +47,16 @@ extern "C" fn init(user_data: *mut ffi::c_void) { // a checkerboard image and texture view let img_width: usize = 8; let img_height: usize = 8; + let mut pixels = [0u32; 64]; + for y in 0..img_height { + for x in 0..img_width { + pixels[y * img_width + x] = if 0 == (y ^ x) & 1 { 0xFF_00_00_00 } else { 0xFF_FF_FF_FF }; + } + } let img = sg::make_image(&sg::ImageDesc { width: img_width as i32, height: img_height as i32, - data: { - let mut data = sg::ImageData::new(); - let mut res = [0u32; 64]; - for y in 0..img_height { - for x in 0..img_width { - res[y * img_width + x] = if 0 == (y ^ x) & 1 { 0xFF_00_00_00 } else { 0xFF_FF_FF_FF }; - } - } - data.mip_levels[0] = sg::value_as_range(&res); - data - }, + data: sg::slice_as_range(&pixels), ..Default::default() }); state.tex_view = sg::make_view(&sg::ViewDesc { diff --git a/examples/texcube/texcube.rs b/examples/texcube/texcube.rs index 33622ae..7dd7bc6 100644 --- a/examples/texcube/texcube.rs +++ b/examples/texcube/texcube.rs @@ -107,8 +107,8 @@ extern "C" fn init(user_data: *mut ffi::c_void) { 0xFFFFFFFF, 0xFF000000, 0xFFFFFFFF, 0xFF000000, 0xFF000000, 0xFFFFFFFF, 0xFF000000, 0xFFFFFFFF, ]; // NOTE: SLOT_tex is provided by shader code generation - let mut image_desc = sg::ImageDesc { width: 4, height: 4, ..Default::default() }; - image_desc.data.mip_levels[0] = sg::slice_as_range(&pixels); + let image_desc = + sg::ImageDesc { width: 4, height: 4, data: sg::slice_as_range(&pixels), ..Default::default() }; state.bind.views[shader::VIEW_TEX] = sg::make_view(&sg::ViewDesc { texture: sg::TextureViewDesc { image: sg::make_image(&image_desc), ..Default::default() }, ..Default::default() diff --git a/src/gfx.rs b/src/gfx.rs index de62442..58eaef4 100644 --- a/src/gfx.rs +++ b/src/gfx.rs @@ -329,6 +329,7 @@ pub struct Features { pub vertexformat_int10_n2: bool, pub copy_buffer_to_image_relaxed_buffer_type: bool, pub copy_buffer_to_image_relaxed_bytes_per_row: bool, + pub copy_image_to_image_relaxed: bool, pub gl_texture_views: bool, } impl Features { @@ -347,6 +348,7 @@ impl Features { vertexformat_int10_n2: false, copy_buffer_to_image_relaxed_buffer_type: false, copy_buffer_to_image_relaxed_bytes_per_row: false, + copy_image_to_image_relaxed: false, gl_texture_views: false, } } @@ -1288,7 +1290,6 @@ pub struct ImageUsage { pub write_transient: bool, pub copy_src: bool, pub copy_dst: bool, - pub immutable: bool, } impl ImageUsage { pub const fn new() -> Self { @@ -1301,7 +1302,6 @@ impl ImageUsage { write_transient: false, copy_src: false, copy_dst: false, - immutable: false, } } } @@ -1333,21 +1333,6 @@ impl Default for ViewType { } #[repr(C)] #[derive(Copy, Clone, Debug)] -pub struct ImageData { - pub mip_levels: [Range; 16], -} -impl ImageData { - pub const fn new() -> Self { - Self { mip_levels: [Range::new(); 16] } - } -} -impl Default for ImageData { - fn default() -> Self { - Self::new() - } -} -#[repr(C)] -#[derive(Copy, Clone, Debug)] pub struct ImageExtent { pub width: i32, pub height: i32, @@ -1528,6 +1513,23 @@ impl Default for CopyBufferToImageDesc { } #[repr(C)] #[derive(Copy, Clone, Debug)] +pub struct CopyImageToImageDesc { + pub src: ImageLocation, + pub dst: ImageLocation, + pub size: ImageExtent, +} +impl CopyImageToImageDesc { + pub const fn new() -> Self { + Self { src: ImageLocation::new(), dst: ImageLocation::new(), size: ImageExtent::new() } + } +} +impl Default for CopyImageToImageDesc { + fn default() -> Self { + Self::new() + } +} +#[repr(C)] +#[derive(Copy, Clone, Debug)] pub struct ImageDesc { pub _start_canary: u32, pub _type: ImageType, @@ -1538,7 +1540,7 @@ pub struct ImageDesc { pub num_mipmaps: i32, pub pixel_format: PixelFormat, pub sample_count: i32, - pub data: ImageData, + pub data: Range, pub label: *const core::ffi::c_char, pub gl_texture: u32, pub gl_texture_target: u32, @@ -1559,7 +1561,7 @@ impl ImageDesc { num_mipmaps: 0, pixel_format: PixelFormat::new(), sample_count: 0, - data: ImageData::new(), + data: Range::new(), label: core::ptr::null(), gl_texture: 0, gl_texture_target: 0, @@ -2337,6 +2339,7 @@ pub struct TraceHooks { pub seal_image: Option, pub copy_buffer_to_buffer: Option, pub copy_buffer_to_image: Option, + pub copy_image_to_image: Option, pub begin_pass: Option, pub apply_viewport: Option, pub apply_scissor_rect: Option, @@ -2406,6 +2409,7 @@ impl TraceHooks { seal_image: None, copy_buffer_to_buffer: None, copy_buffer_to_image: None, + copy_image_to_image: None, begin_pass: None, apply_viewport: None, apply_scissor_rect: None, @@ -3002,6 +3006,7 @@ pub struct FrameStatsVk { pub num_delete_queue_collected: u32, pub num_cmd_copy_buffer: u32, pub num_cmd_copy_buffer_to_image: u32, + pub num_cmd_copy_image: u32, pub num_cmd_set_descriptor_buffer_offsets: u32, pub size_descriptor_buffer_writes: u32, } @@ -3016,6 +3021,7 @@ impl FrameStatsVk { num_delete_queue_collected: 0, num_cmd_copy_buffer: 0, num_cmd_copy_buffer_to_image: 0, + num_cmd_copy_image: 0, num_cmd_set_descriptor_buffer_offsets: 0, size_descriptor_buffer_writes: 0, } @@ -3112,9 +3118,11 @@ pub struct FrameStats { pub num_seal_image: u32, pub num_copy_buffer_to_buffer: u32, pub num_copy_buffer_to_image: u32, + pub num_copy_image_to_image: u32, pub size_apply_uniforms: u32, pub size_copy_buffer_to_buffer: u32, pub size_copy_buffer_to_image: u32, + pub size_copy_image_to_image: u32, pub buffers: FrameResourceStats, pub images: FrameResourceStats, pub samplers: FrameResourceStats, @@ -3148,9 +3156,11 @@ impl FrameStats { num_seal_image: 0, num_copy_buffer_to_buffer: 0, num_copy_buffer_to_image: 0, + num_copy_image_to_image: 0, size_apply_uniforms: 0, size_copy_buffer_to_buffer: 0, size_copy_buffer_to_image: 0, + size_copy_image_to_image: 0, buffers: FrameResourceStats::new(), images: FrameResourceStats::new(), samplers: FrameResourceStats::new(), @@ -3374,6 +3384,10 @@ pub enum LogItem { CopyBufferToImageDstAlive, CopyBufferToImageSrcValid, CopyBufferToImageDstValid, + CopyImageToImageSrcAlive, + CopyImageToImageDstAlive, + CopyImageToImageSrcValid, + CopyImageToImageDstValid, ShaderdescTooManyVertexstageTextures, ShaderdescTooManyFragmentstageTextures, ShaderdescTooManyComputestageTextures, @@ -3409,41 +3423,47 @@ pub enum LogItem { ValidateBufferdescExpectData, ValidateBufferdescStoragebufferSupported, ValidateBufferdescStoragebufferSizeMultiple4, - ValidateImagedataNodata, - ValidateImagedataDataSize, + ValidateImagedescDataSize, ValidateImagedescCanary, - ValidateImagedescImmutableVsWritable, - ValidateImagedescWriteunsealedVsImmutable, - ValidateImagedescWriteunsealedVsAttachment, - ValidateImagedescWritetransientVsAttachment, + ValidateImagedescAnyAttachmentVsCopydst, + ValidateImagedescAnyAttachmentVsInitialdata, + ValidateImagedescAnyAttachmentVsWritetransient, + ValidateImagedescAnyAttachmentVsWriteunsealed, + ValidateImagedescColorAttachmentPixelformat, + ValidateImagedescColorAttachmentVsDepthstencilAttachment, + ValidateImagedescResolveAttachmentExpectNoMsaa, + ValidateImagedescResolveAttachmentVsDepthstencilAttachment, + ValidateImagedescCopydstVsInitialdata, + ValidateImagedescDepthformat3dImage, + ValidateImagedescDepthformatAttachmentOrCopydst, + ValidateImagedescDepthstencilAttachmentPixelformat, + ValidateImagedescDepthstencilAttachmentVsStorageimage, + ValidateImagedescStorageimageVsInitialdata, + ValidateImagedescStorageimageVsWritetransient, + ValidateImagedescWritetransientVsCopydst, + ValidateImagedescWritetransientVsInitialdata, ValidateImagedescWritetransientVsInjected, - ValidateImagedescCopydstVsAttachment, - ValidateImagedescAttachmentColorDepthStencil, + ValidateImagedescWriteunsealedVsCopydst, + ValidateImagedescWriteunsealedVsInitialdata, + ValidateImagedescWriteunsealedVsWritetransient, + ValidateImagedescInitialDataRequired, + ValidateImagedescInjectedExpectNoData, + ValidateImagedescNoDataButSizeSet, ValidateImagedescImagetype2dNumslices, ValidateImagedescImagetypeCubeNumslices, ValidateImagedescImagetypeArrayNumslices, ValidateImagedescImagetype3dNumslices, - ValidateImagedescNumslices, ValidateImagedescWidth, ValidateImagedescHeight, - ValidateImagedescNonrtPixelformat, ValidateImagedescMsaaButNoAttachment, - ValidateImagedescDepth3dImage, - ValidateImagedescAttachmentExpectImmutable, - ValidateImagedescAttachmentExpectNoData, ValidateImagedescAttachmentPixelformat, - ValidateImagedescAttachmentResolveExpectNoMsaa, ValidateImagedescAttachmentNoMsaaSupport, ValidateImagedescAttachmentMsaaNumMipmaps, ValidateImagedescAttachmentMsaa3dImage, ValidateImagedescAttachmentMsaaCubeImage, ValidateImagedescAttachmentMsaaArrayImage, - ValidateImagedescStorageimageExpectImmutable, - ValidateImagedescStorageimageExpectNoData, ValidateImagedescStorageimagePixelformat, ValidateImagedescStorageimageExpectNoMsaa, - ValidateImagedescInjectedNoData, - ValidateImagedescWritableNoData, ValidateSamplerdescCanary, ValidateSamplerdescAnistropicRequiresLinearFiltering, ValidateShaderdescCanary, @@ -3607,7 +3627,6 @@ pub enum LogItem { ValidateBeginpassResolveattachmentviewImageAlive, ValidateBeginpassResolveattachmentviewImageValid, ValidateBeginpassResolveattachmentviewSizes, - ValidateBeginpassDepthstencilattachmentviewsContinuous, ValidateBeginpassDepthstencilattachmentviewAlive, ValidateBeginpassDepthstencilattachmentviewValid, ValidateBeginpassDepthstencilattachmentviewType, @@ -3711,7 +3730,6 @@ pub enum LogItem { ValidateDispatchNumgroupsx, ValidateDispatchNumgroupsy, ValidateDispatchNumgroupsz, - ValidateDispatchRequiredBindingsOrUniformsMissing, ValidateDispatchWriteBufferTransientMissing, ValidateDispatchWriteImageTransientMissing, ValidateWritebufferunsealedUsage, @@ -3727,6 +3745,7 @@ pub enum LogItem { ValidateWriteimageunsealedUsage, ValidateWriteimagetransientUsage, ValidateWriteimagetransientWriteBeforeBind, + ValidateWriteimagetransientWriteBeforeCopy, ValidateWriteimageSrcDataPointer, ValidateWriteimageSrcDataSize, ValidateWriteimageBytesperrow, @@ -3760,6 +3779,7 @@ pub enum LogItem { ValidateCopybuffertobufferSrcOverflow, ValidateCopybuffertobufferDstOverflow, ValidateCopybuffertobufferWebgl2IndexBuffer, + ValidateCopybuffertoimageDepthFormatNotAllowed, ValidateCopybuffertoimageWebgl2IndexBuffer, ValidateCopybuffertoimageBytesperrowTooSmall, ValidateCopybuffertoimageBytespersliceTooSmall, @@ -3787,6 +3807,45 @@ pub enum LogItem { ValidateCopybuffertoimageDstWidthOverflow, ValidateCopybuffertoimageDstHeightOverflow, ValidateCopybuffertoimageDstNumslicesOverflow, + ValidateCopyimagetoimageSrcVsDstImage, + ValidateCopyimagetoimageInsidePass, + ValidateCopyimagetoimageCopySrc, + ValidateCopyimagetoimageCopyDst, + ValidateCopyimagetoimage3dVsOther, + ValidateCopyimagetoimageSrcVsDstFormat, + ValidateCopyimagetoimageSrcVsDstSampleCount, + ValidateCopyimagetoimageRenderable, + ValidateCopyimagetoimageMsaa, + ValidateCopyimagetoimageWholeMiplevel, + ValidateCopyimagetoimageSrcVsDstSize, + ValidateCopyimagetoimageSrcMiplevel, + ValidateCopyimagetoimageSrcWidth, + ValidateCopyimagetoimageSrcHeight, + ValidateCopyimagetoimageDstMiplevel, + ValidateCopyimagetoimageDstWidth, + ValidateCopyimagetoimageDstHeight, + ValidateCopyimagetoimageSrcWidthMultiple, + ValidateCopyimagetoimageSrcHeightMultiple, + ValidateCopyimagetoimageDstWidthMultiple, + ValidateCopyimagetoimageDstHeightMultiple, + ValidateCopyimagetoimageSrcNumslices, + ValidateCopyimagetoimageDstNumslices, + ValidateCopyimagetoimageSrcXRange, + ValidateCopyimagetoimageDstXRange, + ValidateCopyimagetoimageSrcYRange, + ValidateCopyimagetoimageDstYRange, + ValidateCopyimagetoimageSrcXAlignment, + ValidateCopyimagetoimageDstXAlignment, + ValidateCopyimagetoimageSrcYAlignment, + ValidateCopyimagetoimageDstYAlignment, + ValidateCopyimagetoimageSrcSliceRange, + ValidateCopyimagetoimageDstSliceRange, + ValidateCopyimagetoimageSrcWidthOverflow, + ValidateCopyimagetoimageDstWidthOverflow, + ValidateCopyimagetoimageSrcHeightOverflow, + ValidateCopyimagetoimageDstHeightOverflow, + ValidateCopyimagetoimageSrcNumslicesOverflow, + ValidateCopyimagetoimageDstNumslicesOverflow, ValidationFailed, } impl LogItem { @@ -4520,6 +4579,7 @@ pub mod ffi { pub fn sg_seal_image(img: Image); pub fn sg_copy_buffer_to_buffer(desc: *const CopyBufferToBufferDesc); pub fn sg_copy_buffer_to_image(desc: *const CopyBufferToImageDesc); + pub fn sg_copy_image_to_image(desc: *const CopyImageToImageDesc); pub fn sg_query_desc() -> Desc; pub fn sg_query_backend() -> Backend; pub fn sg_query_features() -> Features; @@ -4811,6 +4871,10 @@ pub fn copy_buffer_to_image(desc: &CopyBufferToImageDesc) { unsafe { ffi::sg_copy_buffer_to_image(desc) } } #[inline] +pub fn copy_image_to_image(desc: &CopyImageToImageDesc) { + unsafe { ffi::sg_copy_image_to_image(desc) } +} +#[inline] pub fn query_desc() -> Desc { unsafe { ffi::sg_query_desc() } } diff --git a/src/sokol/c/sokol_debugtext.h b/src/sokol/c/sokol_debugtext.h index bd91cb9..d868bed 100644 --- a/src/sokol/c/sokol_debugtext.h +++ b/src/sokol/c/sokol_debugtext.h @@ -4174,7 +4174,7 @@ static void _sdtx_setup_common(void) { img_desc.width = 256 * 8; img_desc.height = SDTX_MAX_FONTS * 8; img_desc.pixel_format = SG_PIXELFORMAT_R8; - img_desc.data.mip_levels[0] = SG_RANGE(_sdtx.font_pixels); + img_desc.data = SG_RANGE(_sdtx.font_pixels); img_desc.label = "sdtx-font-texture"; _sdtx.font_img = sg_make_image(&img_desc); SOKOL_ASSERT(SG_INVALID_ID != _sdtx.font_img.id); diff --git a/src/sokol/c/sokol_gfx.h b/src/sokol/c/sokol_gfx.h index 819e07d..b0fd136 100644 --- a/src/sokol/c/sokol_gfx.h +++ b/src/sokol/c/sokol_gfx.h @@ -1350,11 +1350,7 @@ uint8_t mip1_data[] = { ... }; // ... more mip levels const sg_image img = sg_make_image(&(sg_image_desc){ - .data.mip_levels = { - [0] = SG_RANGE(mip0_data), - [1] = SG_RANGE(mip1_data), - // ... - }, + .data.= SG_RANGE(image_data), // ... more create options }); ``` @@ -2626,6 +2622,7 @@ typedef struct sg_features { bool vertexformat_int10_n2; // SG_VERTEXFORMAT_INT10_N2 is supported bool copy_buffer_to_image_relaxed_buffer_type; // if false: sg_copy_buffer_to_image source buffer must be .usage.staging_buffer (d3d11 restriction) bool copy_buffer_to_image_relaxed_bytes_per_row; // if false: sg_copy_buffer_to_image .src.bytes_per_row must be a multiple of 256 (webgpu restriction) + bool copy_image_to_image_relaxed; // if false: sg_copy_image_to_image only works for renderable and non-msaa pixel formats bool gl_texture_views; // supports 'proper' texture views (GL 4.3+) } sg_features; @@ -3731,9 +3728,6 @@ typedef struct sg_buffer_desc { .copy_dst (default: false) the image is going to be used as destination in an `sg_copy_buffer_to_image()` call - .immutable (default: true, deprecated) - the image content cannot be updated from the CPU side - (but may be updated by the GPU in a render- or compute-pass) Note that creating a texture view from the image to be used for texture-sampling in vertex-, fragment- or compute-shaders @@ -3748,8 +3742,6 @@ typedef struct sg_image_usage { bool write_transient; bool copy_src; bool copy_dst; - // deprecated - bool immutable; } sg_image_usage; /* @@ -3768,43 +3760,6 @@ typedef enum sg_view_type { _SG_VIEWTYPE_FORCE_U32 = 0x7FFFFFFF } sg_view_type; -/* - sg_image_data - - Defines the content of an image through an array of sg_range structs, each - range pointing to the pixel data for one mip-level. For array-, cubemap- and - 3D-images each mip-level contains all slice-surfaces for that mip-level in a - single tightly packed memory block. - - The size of a single surface in a mip-level for a regular 2D texture - can be computed via: - - sg_query_surface_pitch(pixel_format, mip_width, mip_height, 1); - - For array- and 3d-images the size of a single miplevel is: - - num_slices * sg_query_surface_pitch(pixel_format, mip_width, mip_height, 1); - - For cubemap-images the size of a single mip-level is: - - 6 * sg_query_surface_pitch(pixel_format, mip_width, mip_height, 1); - - The order of cubemap-faces is in a mip-level data chunk is: - - [0] => +X - [1] => -X - [2] => +Y - [3] => -Y - [4] => +Z - [5] => -Z - - NOTE: for more flexible resource initialization of immutable images - also consider the sg_write_image_unsealed() function! -*/ -typedef struct sg_image_data { - sg_range mip_levels[SG_MAX_MIPMAPS]; -} sg_image_data; - /* sg_image_extent @@ -4072,6 +4027,17 @@ typedef struct sg_copy_buffer_to_image_desc { sg_image_extent size; } sg_copy_buffer_to_image_desc; +/* + sg_copy_image_to_image_desc + + FIXME +*/ +typedef struct sg_copy_image_to_image_desc { + sg_image_location src; + sg_image_location dst; + sg_image_extent size; +} sg_copy_image_to_image_desc; + /* sg_image_desc @@ -4080,14 +4046,14 @@ typedef struct sg_copy_buffer_to_image_desc { The default configuration is: .type SG_IMAGETYPE_2D - .usage .immutable = true + .usage { .texture = true } .width 0 (must be set to >0) .height 0 (must be set to >0) .num_slices 1 (3D textures: depth; array textures: number of layers) .num_mipmaps 1 .pixel_format SG_PIXELFORMAT_RGBA8 for textures, or sg_desc.environment.defaults.color_format for render targets .sample_count 1 for textures, or sg_desc.environment.defaults.sample_count for render targets - .data an sg_image_data struct to define the initial content + .data optional ptr/size pair with complete and tightly packed initial data .label 0 (optional string label for trace hooks) Q: Why is the default sample_count for render targets identical with the @@ -4101,9 +4067,37 @@ typedef struct sg_copy_buffer_to_image_desc { NOTE: - Regular images used as texture binding with usage.immutable must be fully - initialized by providing a valid .data member which points to initialization - data. + When initial data is provided via sg_image_desc.data, it must be the complete + and tightly packed data for all mip levels and slices in one continuous block + of memory. For a more flexible initialization of the initial image content, + use sg_write_image_unsealed(). + + The size of a single surface in a mip-level for a regular 2D texture + can be computed via: + + sg_query_surface_pitch(pixel_format, mip_width, mip_height, 1); + + For array-images the size of a single miplevel is: + + num_slices * sg_query_surface_pitch(pixel_format, mip_width, mip_height, 1); + + For 3d-images the size of a single miplevel is: + + mip_slices * sg_query_surface_pitch(pixel_format, mip_width, mip_height, 1); + + For cubemap-images the size of a single mip-level is: + + 6 * sg_query_surface_pitch(pixel_format, mip_width, mip_height, 1); + + The order of cubemap-faces is in a mip-level data chunk is: + + [0] => +X + [1] => -X + [2] => +Y + [3] => -Y + [4] => +Z + [5] => -Z + Images with usage.*_attachment or usage.storage_image must *not* be created with initial content. Be aware that the initial @@ -4115,8 +4109,9 @@ typedef struct sg_copy_buffer_to_image_desc { The following struct members allow to inject your own GL, Metal or D3D11 textures into sokol_gfx: - .gl_textures[SG_NUM_INFLIGHT_FRAMES] - .mtl_textures[SG_NUM_INFLIGHT_FRAMES] + .gl_texture + .gl_texture_target + .mtl_texture .d3d11_texture .wgpu_texture @@ -4137,7 +4132,7 @@ typedef struct sg_image_desc { int num_mipmaps; sg_pixel_format pixel_format; int sample_count; - sg_image_data data; + sg_range data; const char* label; // optionally inject backend-specific resources uint32_t gl_texture; @@ -4740,6 +4735,7 @@ typedef struct sg_trace_hooks { void (*seal_image)(sg_image img, void* user_data); void (*copy_buffer_to_buffer)(const sg_copy_buffer_to_buffer_desc* desc, void* user_data); void (*copy_buffer_to_image)(const sg_copy_buffer_to_image_desc* desc, void* user_data); + void (*copy_image_to_image)(const sg_copy_image_to_image_desc* desc, void* user_data); void (*begin_pass)(const sg_pass* pass, void* user_data); void (*apply_viewport)(int x, int y, int width, int height, bool origin_top_left, void* user_data); void (*apply_scissor_rect)(int x, int y, int width, int height, bool origin_top_left, void* user_data); @@ -5007,6 +5003,7 @@ typedef struct sg_frame_stats_vk { uint32_t num_delete_queue_collected; uint32_t num_cmd_copy_buffer; uint32_t num_cmd_copy_buffer_to_image; + uint32_t num_cmd_copy_image; uint32_t num_cmd_set_descriptor_buffer_offsets; uint32_t size_descriptor_buffer_writes; } sg_frame_stats_vk; @@ -5056,10 +5053,12 @@ typedef struct sg_frame_stats { uint32_t num_seal_image; uint32_t num_copy_buffer_to_buffer; uint32_t num_copy_buffer_to_image; + uint32_t num_copy_image_to_image; uint32_t size_apply_uniforms; uint32_t size_copy_buffer_to_buffer; uint32_t size_copy_buffer_to_image; + uint32_t size_copy_image_to_image; sg_frame_resource_stats buffers; sg_frame_resource_stats images; @@ -5269,6 +5268,10 @@ typedef struct sg_stats { _SG_LOGITEM_XMACRO(COPY_BUFFER_TO_IMAGE_DST_ALIVE, "sg_copy_buffer_to_image: dst image no longer alive") \ _SG_LOGITEM_XMACRO(COPY_BUFFER_TO_IMAGE_SRC_VALID, "sg_copy_buffer_to_image: src buffer not in state SG_RESOURCESTATE_VALID") \ _SG_LOGITEM_XMACRO(COPY_BUFFER_TO_IMAGE_DST_VALID, "sg_copy_buffer_to_image: dst image not in state SG_RESOURCESTATE_VALID") \ + _SG_LOGITEM_XMACRO(COPY_IMAGE_TO_IMAGE_SRC_ALIVE, "sg_copy_image_to_image: src image no longer alive") \ + _SG_LOGITEM_XMACRO(COPY_IMAGE_TO_IMAGE_DST_ALIVE, "sg_copy_image_to_image: dst image no longer alive") \ + _SG_LOGITEM_XMACRO(COPY_IMAGE_TO_IMAGE_SRC_VALID, "sg_copy_image_to_image: src image not in state SG_RESOURCESTATE_VALID") \ + _SG_LOGITEM_XMACRO(COPY_IMAGE_TO_IMAGE_DST_VALID, "sg_copy_image_to_image: dst image not in state SG_RESOURCESTATE_VALID") \ _SG_LOGITEM_XMACRO(SHADERDESC_TOO_MANY_VERTEXSTAGE_TEXTURES, "sg_shader_desc: too many texture bindings on vertex shader stage (sg_limits.max_texture_bindings_per_stage)") \ _SG_LOGITEM_XMACRO(SHADERDESC_TOO_MANY_FRAGMENTSTAGE_TEXTURES, "sg_shader_desc: too many texture bindings on fragment shader stage (sg_limits.max_texture_bindings_per_stage)") \ _SG_LOGITEM_XMACRO(SHADERDESC_TOO_MANY_COMPUTESTAGE_TEXTURES, "sg_shader_desc: too many texture bindings on compute shader stage (sg_limits.max_texture_bindings_per_stage)") \ @@ -5304,41 +5307,47 @@ typedef struct sg_stats { _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_EXPECT_DATA, "sg_buffer_desc: buffer content can never be initialized (provide .data, or set .usage.write_unsealed, .usage.write_transient or .usage.copy_dst)") \ _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_STORAGEBUFFER_SUPPORTED, "storage buffers not supported by the backend 3D API (requires OpenGL >= 4.3)") \ _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_STORAGEBUFFER_SIZE_MULTIPLE_4, "size of storage buffers must be a multiple of 4") \ - _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDATA_NODATA, "sg_image_data: no data (.ptr and/or .size is zero)") \ - _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDATA_DATA_SIZE, "sg_image_data: data size doesn't match expected surface size") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_DATA_SIZE, "sg_image_desc.data: data size doesn't match expected size (complete tightly packed size across all mip levels and slices) ") \ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_CANARY, "sg_image_desc not initialized") \ - _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_IMMUTABLE_VS_WRITABLE, "sg_image_desc.usage: only one of .immutable, .write_transient, .copy_dst can be true") \ - _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_WRITEUNSEALED_VS_IMMUTABLE, "sg_image_desc.usage: .write_unsealed only allowed for .immutable images") \ - _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_WRITEUNSEALED_VS_ATTACHMENT, "sg_image_desc.usage: .write_unsealed not allowed for images with attachment usage") \ - _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_WRITETRANSIENT_VS_ATTACHMENT, "sg_image_desc.usage: .write_transient not allowed for images with attachment usage") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_ANY_ATTACHMENT_VS_COPYDST, "sg_image_desc.usage: attachments cannot have usage .copy_dst") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_ANY_ATTACHMENT_VS_INITIALDATA, "sg_image_desc.data: attachments cannot have initial data") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_ANY_ATTACHMENT_VS_WRITETRANSIENT, "sg_image_desc.usage: attachments cannot have usage .write_transient") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_ANY_ATTACHMENT_VS_WRITEUNSEALED, "sg_image_desc.usage: attachments cannot have usage .write_unsealed") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_COLOR_ATTACHMENT_PIXELFORMAT, "sg_image_desc.pixel_format: pixel format is not valid for color attachments") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_COLOR_ATTACHMENT_VS_DEPTHSTENCIL_ATTACHMENT, "sg_image_desc.usage: .color_attachment cannot be combined with .depth_stencil_attachment") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_RESOLVE_ATTACHMENT_EXPECT_NO_MSAA, "sg_image_desc: resolve attachments cannot be multisampled") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_RESOLVE_ATTACHMENT_VS_DEPTHSTENCIL_ATTACHMENT, "sg_image_desc.usage: .resolve_attachment cannot be combined with .depth_stencil_attachment") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_COPYDST_VS_INITIALDATA, "sg_image_desc.data: images with .usage.copy_dst cannot have initial data") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_DEPTHFORMAT_3D_IMAGE, "sg_image_desc.type: 3D images cannot have a depth- or depth-stencil pixel format") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_DEPTHFORMAT_ATTACHMENT_OR_COPYDST, "sg_image_desc: images with depth- or depth-stencil format must have .usage.depth_stencil_attachment or .usage.copy_dst") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_DEPTHSTENCIL_ATTACHMENT_PIXELFORMAT, "sg_image_desc.pixel_format: the image's pixel format isn't a valid depth- or depth-stencil format") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_DEPTHSTENCIL_ATTACHMENT_VS_STORAGEIMAGE, "sg_image_desc.usage: .usage.storage_image cannot be used together with .usage.depth_stencil_attachment") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_STORAGEIMAGE_VS_INITIALDATA, "sg_image_desc: images with .usage.storage_image cannot have initial data") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_STORAGEIMAGE_VS_WRITETRANSIENT, "sg_image_desc.usage: .storage_image cannot be combined with .write_transient") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_WRITETRANSIENT_VS_COPYDST, "sg_image_desc.usage: .write_transient cannot be combined with .copy_dst") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_WRITETRANSIENT_VS_INITIALDATA, "sg_image_desc: images with .usage.write_transient cannot have initial data") \ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_WRITETRANSIENT_VS_INJECTED, "sg_image_desc.usage: cannot inject backend-native textures for .write_transient usage") \ - _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_COPYDST_VS_ATTACHMENT, "sg_image_desc.usage: .copy_dst not allowed for images with attachment usage") \ - _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_ATTACHMENT_COLOR_DEPTH_STENCIL, "sg_image_desc.usage: only one of .color_attachment and .depth_stencil_attachment can be true") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_WRITEUNSEALED_VS_COPYDST, "sg_image_desc.usage: .write_unsealed cannot be combined with .copy_dst") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_WRITEUNSEALED_VS_INITIALDATA, "sg_image_desc: images with .usage.write_unsealed cannot have initial data") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_WRITEUNSEALED_VS_WRITETRANSIENT, "sg_image_desc.usage: .write_unsealed cannot be combined with .write_transient") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_INITIAL_DATA_REQUIRED, "sg_image_desc.data: the image's usage flag combination requires initial data to be provided") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_INJECTED_EXPECT_NO_DATA, "sg_image_desc: sg_image_desc.data cannot be used to write data into injected backend API resources") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_NO_DATA_BUT_SIZE_SET, "sg_image_desc: .data.size is > 0 but .data.ptr is null") \ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_IMAGETYPE_2D_NUMSLICES, "sg_image_desc.num_slices must be exactly 1 for SG_IMAGETYPE_2D") \ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_IMAGETYPE_CUBE_NUMSLICES, "sg_image_desc.num_slices must be exactly 6 for SG_IMAGETYPE_CUBE") \ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_IMAGETYPE_ARRAY_NUMSLICES, "sg_image_desc.num_slices must be ((>= 1) && (<= sg_limits.max_image_array_layers)) for SG_IMAGETYPE_ARRAY") \ - _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_IMAGETYPE_3D_NUMSLICES, "sg_image_desc.num_slices must be ((>= 1) && (<= sg_limits.max_image_size_3d)) for SG_IMAGETYPE_ARRAY") \ - _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_NUMSLICES, "sg_image_desc.num_slices must be > 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_IMAGETYPE_3D_NUMSLICES, "sg_image_desc.num_slices must be ((>= 1) && (<= sg_limits.max_image_size_3d)) for SG_IMAGETYPE_3D") \ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_WIDTH, "sg_image_desc.width must be > 0") \ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_HEIGHT, "sg_image_desc.height must be > 0") \ - _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_NONRT_PIXELFORMAT, "invalid pixel format for non-render-target image") \ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_MSAA_BUT_NO_ATTACHMENT, "non-attachment images cannot be multisampled") \ - _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_DEPTH_3D_IMAGE, "3D images cannot have a depth/stencil image format") \ - _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_ATTACHMENT_EXPECT_IMMUTABLE, "attachment and storage images must be sg_image_usage.immutable") \ - _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_ATTACHMENT_EXPECT_NO_DATA, "render/storage attachment images cannot be initialized with data") \ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_ATTACHMENT_PIXELFORMAT, "invalid pixel format for render attachment image") \ - _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_ATTACHMENT_RESOLVE_EXPECT_NO_MSAA, "resolve attachment images cannot be multisampled") \ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_ATTACHMENT_NO_MSAA_SUPPORT, "multisampling not supported for this pixel format") \ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_ATTACHMENT_MSAA_NUM_MIPMAPS, "multisample images must have num_mipmaps == 1") \ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_ATTACHMENT_MSAA_3D_IMAGE, "3D images cannot have a sample_count > 1") \ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_ATTACHMENT_MSAA_CUBE_IMAGE, "cube images cannot have sample_count > 1") \ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_ATTACHMENT_MSAA_ARRAY_IMAGE, "array images cannot have sample_count > 1") \ - _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_STORAGEIMAGE_EXPECT_IMMUTABLE, "storage images must be sg_image_usage.immutable") \ - _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_STORAGEIMAGE_EXPECT_NO_DATA, "storage images cannot be initialized with data") \ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_STORAGEIMAGE_PIXELFORMAT, "invalid pixel format for storage image") \ _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_STORAGEIMAGE_EXPECT_NO_MSAA, "storage images cannot be multisampled") \ - _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_INJECTED_NO_DATA, "images with injected textures cannot be initialized with data") \ - _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_WRITABLE_NO_DATA, "cpu-writable images cannot be initialized with data") \ _SG_LOGITEM_XMACRO(VALIDATE_SAMPLERDESC_CANARY, "sg_sampler_desc not initialized") \ _SG_LOGITEM_XMACRO(VALIDATE_SAMPLERDESC_ANISTROPIC_REQUIRES_LINEAR_FILTERING, "sg_sampler_desc.max_anisotropy > 1 requires min/mag/mipmap_filter to be SG_FILTER_LINEAR") \ _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_CANARY, "sg_shader_desc not initialized") \ @@ -5502,7 +5511,6 @@ typedef struct sg_stats { _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_RESOLVEATTACHMENTVIEW_IMAGE_ALIVE, "sg_begin_pass: resolve attachment view's image object is uninitialized or no longer alive") \ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_RESOLVEATTACHMENTVIEW_IMAGE_VALID, "sg_begin_pass: resolve attachment view's image is not in valid state (SG_RESOURCESTATE_VALID)") \ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_RESOLVEATTACHMENTVIEW_SIZES, "sg_begin_pass: all attachments must have the same width and height") \ - _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_DEPTHSTENCILATTACHMENTVIEWS_CONTINUOUS, "sg_begin_pass: color attachment view array must be continuous") \ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_DEPTHSTENCILATTACHMENTVIEW_ALIVE, "sg_begin_pass: depth-stencil attachment view no longer alive") \ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_DEPTHSTENCILATTACHMENTVIEW_VALID, "sg_begin_pass: depth-stencil attachment view not in valid state (SG_RESOURCESTATE_VALID)") \ _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_DEPTHSTENCILATTACHMENTVIEW_TYPE, "sg_begin_pass: depth-stencil attachment view has wrong type (must be sg_view_desc.depth_stencil_attachment)") \ @@ -5606,22 +5614,22 @@ typedef struct sg_stats { _SG_LOGITEM_XMACRO(VALIDATE_DISPATCH_NUMGROUPSX, "sg_dispatch: num_groups_x must be >=0 and <65536") \ _SG_LOGITEM_XMACRO(VALIDATE_DISPATCH_NUMGROUPSY, "sg_dispatch: num_groups_y must be >=0 and <65536") \ _SG_LOGITEM_XMACRO(VALIDATE_DISPATCH_NUMGROUPSZ, "sg_dispatch: num_groups_z must be >=0 and <65536") \ - _SG_LOGITEM_XMACRO(VALIDATE_DISPATCH_REQUIRED_BINDINGS_OR_UNIFORMS_MISSING, "sg_dispatch: call to sg_apply_bindings() and/or sg_apply_uniforms() missing after sg_apply_pipeline()") \ _SG_LOGITEM_XMACRO(VALIDATE_DISPATCH_WRITE_BUFFER_TRANSIENT_MISSING, "sg_dispatch: a bound usage.write_transient buffer hasn't been written this frame (missing sg_write_buffer_transient call)") \ _SG_LOGITEM_XMACRO(VALIDATE_DISPATCH_WRITE_IMAGE_TRANSIENT_MISSING, "sg_dispatch: a bound usage.write_transient image hasn't been written this frame (missing sg_write_image_transient call)") \ _SG_LOGITEM_XMACRO(VALIDATE_WRITEBUFFERUNSEALED_USAGE, "sg_write_buffer_unsealed: buffer usage must be .write_unsealed") \ _SG_LOGITEM_XMACRO(VALIDATE_WRITEBUFFERTRANSIENT_USAGE, "sg_write_buffer_transient: buffer usage must be .write_transient") \ _SG_LOGITEM_XMACRO(VALIDATE_WRITEBUFFERTRANSIENT_WRITE_BEFORE_BIND, "sg_write_buffer_transient: cannot be called after buffer has already been bound in this frame") \ - _SG_LOGITEM_XMACRO(VALIDATE_WRITEBUFFERTRANSIENT_WRITE_BEFORE_COPY, "sg_write_buffer_transient: cannot be called after buffer has been copied from") \ + _SG_LOGITEM_XMACRO(VALIDATE_WRITEBUFFERTRANSIENT_WRITE_BEFORE_COPY, "sg_write_buffer_transient: cannot be called after buffer has been copied from in this frame") \ _SG_LOGITEM_XMACRO(VALIDATE_WRITEBUFFERTRANSIENT_DST_OFFSET_ALIGNMENT, "sg_write_buffer_transient: dst.offset must be a multiple of 4") \ _SG_LOGITEM_XMACRO(VALIDATE_WRITEBUFFER_SRC_DATA_POINTER, "sg_write_buffer_*: desc.src.data.ptr must be valid") \ _SG_LOGITEM_XMACRO(VALIDATE_WRITEBUFFER_SRC_DATA_SIZE, "sg_write_buffer_*: desc.src.data.size must be > 0") \ _SG_LOGITEM_XMACRO(VALIDATE_WRITEBUFFER_SIZE, "sg_write_buffer_*: desc.size must be > 0 and <= desc.src.data.size") \ _SG_LOGITEM_XMACRO(VALIDATE_WRITEBUFFER_WRITE_OVERFLOW, "sg_write_buffer_*: desc.dst.offset + desc.size must be <= buffer size") \ _SG_LOGITEM_XMACRO(VALIDATE_WRITEBUFFER_READ_OVERFLOW, "sg_write_buffer_*: desc.src.offset + desc.size must be <= desc.src.data.size") \ - _SG_LOGITEM_XMACRO(VALIDATE_WRITEIMAGEUNSEALED_USAGE, "sg_write_image_unsealed: image usage must be .immutable && .write_unsealed") \ - _SG_LOGITEM_XMACRO(VALIDATE_WRITEIMAGETRANSIENT_USAGE, "sg_write_image_transient: image usage must be !.immutable && .write_transient") \ + _SG_LOGITEM_XMACRO(VALIDATE_WRITEIMAGEUNSEALED_USAGE, "sg_write_image_unsealed: image usage must be .write_unsealed") \ + _SG_LOGITEM_XMACRO(VALIDATE_WRITEIMAGETRANSIENT_USAGE, "sg_write_image_transient: image usage must be .write_transient") \ _SG_LOGITEM_XMACRO(VALIDATE_WRITEIMAGETRANSIENT_WRITE_BEFORE_BIND, "sg_write_image_transient: cannot be called after image has already been bound in this frame") \ + _SG_LOGITEM_XMACRO(VALIDATE_WRITEIMAGETRANSIENT_WRITE_BEFORE_COPY, "sg_write_image_transient: cannot be called after image has been copied from in this frame") \ _SG_LOGITEM_XMACRO(VALIDATE_WRITEIMAGE_SRC_DATA_POINTER, "sg_write_image_*: desc.src.data.ptr must be valid") \ _SG_LOGITEM_XMACRO(VALIDATE_WRITEIMAGE_SRC_DATA_SIZE, "sg_write_image_*: desc.src.data.size must be > 0") \ _SG_LOGITEM_XMACRO(VALIDATE_WRITEIMAGE_BYTESPERROW, "sg_write_image_*: desc.src.bytes_per_row must be a multiple of the pixel or compression-block size") \ @@ -5655,6 +5663,7 @@ typedef struct sg_stats { _SG_LOGITEM_XMACRO(VALIDATE_COPYBUFFERTOBUFFER_SRC_OVERFLOW, "sg_copy_buffer_to_buffer: (desc.src.offset + desc.size) is greater than desc.src.buffer size") \ _SG_LOGITEM_XMACRO(VALIDATE_COPYBUFFERTOBUFFER_DST_OVERFLOW, "sg_copy_buffer_to_buffer: (desc.dst.offset + desc.size) is greater than desc.dst.buffer size") \ _SG_LOGITEM_XMACRO(VALIDATE_COPYBUFFERTOBUFFER_WEBGL2_INDEX_BUFFER, "sg_copy_buffer_to_buffer: on webgl2, src and dst buffer must both be index buffers or both be non-index buffers (usage.staging_index_buffer counts as index buffer)") \ + _SG_LOGITEM_XMACRO(VALIDATE_COPYBUFFERTOIMAGE_DEPTH_FORMAT_NOT_ALLOWED, "sg_copy_buffer_to_image: cannot copy into a depth- or depth-stencil pixel format") \ _SG_LOGITEM_XMACRO(VALIDATE_COPYBUFFERTOIMAGE_WEBGL2_INDEX_BUFFER, "sg_copy_buffer_to_image: on webgl2, source buffer cannot have usage.index_buffer") \ _SG_LOGITEM_XMACRO(VALIDATE_COPYBUFFERTOIMAGE_BYTESPERROW_TOO_SMALL, "sg_copy_buffer_to_image: desc.src.bytes_per_row is smaller than required by desc.size.width") \ _SG_LOGITEM_XMACRO(VALIDATE_COPYBUFFERTOIMAGE_BYTESPERSLICE_TOO_SMALL, "sg_copy_buffer_to_image: desc.src.bytes_per_slice is smaller than required by desc.size.width and .height") \ @@ -5682,6 +5691,45 @@ typedef struct sg_stats { _SG_LOGITEM_XMACRO(VALIDATE_COPYBUFFERTOIMAGE_DST_WIDTH_OVERFLOW, "sg_copy_buffer_to_image: desc.dst.x + desc.size.width must be <= destination mip level width") \ _SG_LOGITEM_XMACRO(VALIDATE_COPYBUFFERTOIMAGE_DST_HEIGHT_OVERFLOW, "sg_copy_buffer_to_image: desc.dst.y + desc.size.height must be <= destination mip level height") \ _SG_LOGITEM_XMACRO(VALIDATE_COPYBUFFERTOIMAGE_DST_NUMSLICES_OVERFLOW, "sg_copy_buffer_to_image: desc.dst.slice + desc.size.num_slices must be <= destination number of slices in mip level") \ + _SG_LOGITEM_XMACRO(VALIDATE_COPYIMAGETOIMAGE_SRC_VS_DST_IMAGE, "sg_copy_image_to_image: src and dst image cannot be the same") \ + _SG_LOGITEM_XMACRO(VALIDATE_COPYIMAGETOIMAGE_INSIDE_PASS, "sg_copy_image_to_image: must not be called inside a pass") \ + _SG_LOGITEM_XMACRO(VALIDATE_COPYIMAGETOIMAGE_COPY_SRC, "sg_copy_image_to_image: source image must have .copy_src usage") \ + _SG_LOGITEM_XMACRO(VALIDATE_COPYIMAGETOIMAGE_COPY_DST, "sg_copy_image_to_image: destination image must have .copy_dst usage") \ + _SG_LOGITEM_XMACRO(VALIDATE_COPYIMAGETOIMAGE_3D_VS_OTHER, "sg_copy_image_to_image: cannot copy between a 3D image and non-3D image types") \ + _SG_LOGITEM_XMACRO(VALIDATE_COPYIMAGETOIMAGE_SRC_VS_DST_FORMAT, "sg_copy_image_to_image: source and destination pixel format must be identical") \ + _SG_LOGITEM_XMACRO(VALIDATE_COPYIMAGETOIMAGE_SRC_VS_DST_SAMPLE_COUNT, "sg_copy_image_to_image: source and destination image must have identical sample count") \ + _SG_LOGITEM_XMACRO(VALIDATE_COPYIMAGETOIMAGE_RENDERABLE, "sg_copy_image_to_image: image pixel formats must be renderable when sg_features.copy_image_to_image_relaxed is false") \ + _SG_LOGITEM_XMACRO(VALIDATE_COPYIMAGETOIMAGE_MSAA, "sg_copy_image_to_image: images cannot be multi-sampled (sg_image_desc.sample_count > 1) when sg_features.copy_image_to_image_relaxed is false") \ + _SG_LOGITEM_XMACRO(VALIDATE_COPYIMAGETOIMAGE_WHOLE_MIPLEVEL, "sg_copy_image_to_image: when copying between depth(-stencil) or multi-sampled images, the whole mip level must be copied") \ + _SG_LOGITEM_XMACRO(VALIDATE_COPYIMAGETOIMAGE_SRC_VS_DST_SIZE, "sg_copy_image_to_image: when copying between depth(-stencil) or multi-sampled images, the src and dst mip level size must be identical") \ + _SG_LOGITEM_XMACRO(VALIDATE_COPYIMAGETOIMAGE_SRC_MIPLEVEL, "sg_copy_image_to_image: desc.src.mip_level must be >= 0 and less than the number of mipmaps in the source image") \ + _SG_LOGITEM_XMACRO(VALIDATE_COPYIMAGETOIMAGE_SRC_WIDTH, "sg_copy_image_to_image: desc.size.width must be > 0 and <= source image width") \ + _SG_LOGITEM_XMACRO(VALIDATE_COPYIMAGETOIMAGE_SRC_HEIGHT, "sg_copy_image_to_image: desc.size.height must be > 0 and <= source image height") \ + _SG_LOGITEM_XMACRO(VALIDATE_COPYIMAGETOIMAGE_DST_MIPLEVEL, "sg_copy_image_to_image: desc.dst.mip_level must be >= 0 and less than the number of mipmaps in the destination image") \ + _SG_LOGITEM_XMACRO(VALIDATE_COPYIMAGETOIMAGE_DST_WIDTH, "sg_copy_image_to_image: desc.size.width must be > 0 and <= destination image width") \ + _SG_LOGITEM_XMACRO(VALIDATE_COPYIMAGETOIMAGE_DST_HEIGHT, "sg_copy_image_to_image: desc.size.height must be > 0 and <= destination image height") \ + _SG_LOGITEM_XMACRO(VALIDATE_COPYIMAGETOIMAGE_SRC_WIDTH_MULTIPLE, "sg_copy_image_to_image: for compressed pixel formats, desc.size.width must be a multiple of 4, or desc.src.x + desc.size.width must be equal to the source miplevel width") \ + _SG_LOGITEM_XMACRO(VALIDATE_COPYIMAGETOIMAGE_SRC_HEIGHT_MULTIPLE, "sg_copy_image_to_image: for compressed pixel formats, desc.size.height must be a multiple of 4, or desc.src.y + desc.size.height must be equal to the source miplevel height") \ + _SG_LOGITEM_XMACRO(VALIDATE_COPYIMAGETOIMAGE_DST_WIDTH_MULTIPLE, "sg_copy_image_to_image: for compressed pixel formats, desc.size.width must be a multiple of 4, or desc.dst.x + desc.size.width must be equal to the destination miplevel width") \ + _SG_LOGITEM_XMACRO(VALIDATE_COPYIMAGETOIMAGE_DST_HEIGHT_MULTIPLE, "sg_copy_image_to_image: for compressed pixel formats, desc.size.height must be a multiple of 4, or desc.dst.y + desc.size.height must be equal to the destination miplevel height") \ + _SG_LOGITEM_XMACRO(VALIDATE_COPYIMAGETOIMAGE_SRC_NUMSLICES, "sg_copy_image_to_image: desc.size.num_slices must be > 0 and <= source image num slices") \ + _SG_LOGITEM_XMACRO(VALIDATE_COPYIMAGETOIMAGE_DST_NUMSLICES, "sg_copy_image_to_image: desc.size.num_slices must be > 0 and <= destination image num slices") \ + _SG_LOGITEM_XMACRO(VALIDATE_COPYIMAGETOIMAGE_SRC_X_RANGE, "sg_copy_image_to_image: desc.src.x must be >= 0 and < source miplevel width") \ + _SG_LOGITEM_XMACRO(VALIDATE_COPYIMAGETOIMAGE_DST_X_RANGE, "sg_copy_image_to_image: desc.dst.x must be >= 0 and < destination miplevel width") \ + _SG_LOGITEM_XMACRO(VALIDATE_COPYIMAGETOIMAGE_SRC_Y_RANGE, "sg_copy_image_to_image: desc.src.y must be >= 0 and < source miplevel height") \ + _SG_LOGITEM_XMACRO(VALIDATE_COPYIMAGETOIMAGE_DST_Y_RANGE, "sg_copy_image_to_image: desc.dst.y must be >= 0 and < destination miplevel height") \ + _SG_LOGITEM_XMACRO(VALIDATE_COPYIMAGETOIMAGE_SRC_X_ALIGNMENT, "sg_copy_image_to_image: desc.src.x must be a multiple of 4 for compressed pixel formats") \ + _SG_LOGITEM_XMACRO(VALIDATE_COPYIMAGETOIMAGE_DST_X_ALIGNMENT, "sg_copy_image_to_image: desc.dst.x must be a multiple of 4 for compressed pixel formats") \ + _SG_LOGITEM_XMACRO(VALIDATE_COPYIMAGETOIMAGE_SRC_Y_ALIGNMENT, "sg_copy_image_to_image: desc.src.y must be a multiple of 4 for compressed pixel formats") \ + _SG_LOGITEM_XMACRO(VALIDATE_COPYIMAGETOIMAGE_DST_Y_ALIGNMENT, "sg_copy_image_to_image: desc.dst.y must be a multiple of 4 for compressed pixel formats") \ + _SG_LOGITEM_XMACRO(VALIDATE_COPYIMAGETOIMAGE_SRC_SLICE_RANGE, "sg_copy_image_to_image: desc.src.slice must be >= 0 and < source miplevel depth or array/cubemap slices") \ + _SG_LOGITEM_XMACRO(VALIDATE_COPYIMAGETOIMAGE_DST_SLICE_RANGE, "sg_copy_image_to_image: desc.dst.slice must be >= 0 and < destination miplevel depth or array/cubemap slices") \ + _SG_LOGITEM_XMACRO(VALIDATE_COPYIMAGETOIMAGE_SRC_WIDTH_OVERFLOW, "sg_copy_image_to_image: desc.src.x + desc.size.width must be <= source mip level width") \ + _SG_LOGITEM_XMACRO(VALIDATE_COPYIMAGETOIMAGE_DST_WIDTH_OVERFLOW, "sg_copy_image_to_image: desc.dst.x + desc.size.width must be <= destination mip level width") \ + _SG_LOGITEM_XMACRO(VALIDATE_COPYIMAGETOIMAGE_SRC_HEIGHT_OVERFLOW, "sg_copy_image_to_image: desc.src.y + desc.size.height must be <= source mip level height") \ + _SG_LOGITEM_XMACRO(VALIDATE_COPYIMAGETOIMAGE_DST_HEIGHT_OVERFLOW, "sg_copy_image_to_image: desc.dst.y + desc.size.height must be <= destination mip level height") \ + _SG_LOGITEM_XMACRO(VALIDATE_COPYIMAGETOIMAGE_SRC_NUMSLICES_OVERFLOW, "sg_copy_image_to_image: desc.src.slice + desc.size.num_slices must be <= source number of slices in mip level") \ + _SG_LOGITEM_XMACRO(VALIDATE_COPYIMAGETOIMAGE_DST_NUMSLICES_OVERFLOW, "sg_copy_image_to_image: desc.dst.slice + desc.size.num_slices must be <= destination number of slices in mip level") \ _SG_LOGITEM_XMACRO(VALIDATION_FAILED, "validation layer checks failed") \ #define _SG_LOGITEM_XMACRO(item,msg) SG_LOGITEM_##item, @@ -5982,6 +6030,7 @@ SOKOL_GFX_API_DECL void sg_seal_buffer(sg_buffer buf); SOKOL_GFX_API_DECL void sg_seal_image(sg_image img); SOKOL_GFX_API_DECL void sg_copy_buffer_to_buffer(const sg_copy_buffer_to_buffer_desc* desc); SOKOL_GFX_API_DECL void sg_copy_buffer_to_image(const sg_copy_buffer_to_image_desc* desc); +SOKOL_GFX_API_DECL void sg_copy_image_to_image(const sg_copy_image_to_image_desc* desc); // getting information SOKOL_GFX_API_DECL sg_desc sg_query_desc(void); @@ -6298,6 +6347,7 @@ inline void sg_write_buffer_unsealed(const sg_write_buffer_desc& desc) { return inline void sg_write_image_unsealed(const sg_write_image_desc& desc) { return sg_write_image_unsealed(&desc); } inline void sg_copy_buffer_to_buffer(const sg_copy_buffer_to_buffer_desc& desc) { return sg_copy_buffer_to_buffer(&desc); } inline void sg_copy_buffer_to_image(const sg_copy_buffer_to_image_desc& desc) { return sg_copy_buffer_to_image(&desc); } +inline void sg_copy_image_to_image(const sg_copy_image_to_image_desc& desc) { return sg_copy_image_to_image(&desc); } #endif #endif // SOKOL_GFX_INCLUDED @@ -6504,6 +6554,7 @@ inline void sg_copy_buffer_to_image(const sg_copy_buffer_to_image_desc& desc) { #if defined(GL_VERSION_4_3) || defined(_SOKOL_USE_WIN32_GL_LOADER) #define _SOKOL_GL_HAS_COMPUTE (1) #define _SOKOL_GL_HAS_TEXVIEWS (1) + #define _SOKOL_GL_HAS_COPYIMAGE (1) #endif #if defined(GL_VERSION_4_2) || defined(_SOKOL_USE_WIN32_GL_LOADER) #define _SOKOL_GL_HAS_TEXSTORAGE (1) @@ -6540,17 +6591,22 @@ inline void sg_copy_buffer_to_image(const sg_copy_buffer_to_image_desc& desc) { #elif defined(__EMSCRIPTEN__) #define _SOKOL_GL_HAS_TEXSTORAGE (1) #elif defined(__ANDROID__) - #define _SOKOL_GL_HAS_COMPUTE (1) #define _SOKOL_GL_HAS_TEXSTORAGE (1) + #if defined(GL_ES_VERSION_3_1) + #define _SOKOL_GL_HAS_COMPUTE (1) + #endif #if defined(GL_ES_VERSION_3_2) #define _SOKOL_GL_HAS_COLORMASKI (1) + #define _SOKOL_GL_HAS_COPYIMAGE (1) + #define _SOKOL_GL_HAS_BASEVERTEX (1) #endif #elif defined(__linux__) || defined(__unix__) - #define _SOKOL_GL_HAS_COLORMASKI (1) #if defined(SOKOL_GLCORE) + #define _SOKOL_GL_HAS_COLORMASKI (1) #if defined(GL_VERSION_4_3) #define _SOKOL_GL_HAS_COMPUTE (1) #define _SOKOL_GL_HAS_TEXVIEWS (1) + #define _SOKOL_GL_HAS_COPYIMAGE (1) #endif #if defined(GL_VERSION_4_2) #define _SOKOL_GL_HAS_TEXSTORAGE (1) @@ -6564,9 +6620,15 @@ inline void sg_copy_buffer_to_image(const sg_copy_buffer_to_image_desc& desc) { #define _SOKOL_GL_HAS_MSAA_TEXTURES (1) #endif #else - #define _SOKOL_GL_HAS_COMPUTE (1) #define _SOKOL_GL_HAS_TEXSTORAGE (1) - #define _SOKOL_GL_HAS_BASEVERTEX (1) + #if defined(GL_ES_VERSION_3_1) + #define _SOKOL_GL_HAS_COMPUTE (1) + #endif + #if defined(GL_ES_VERSION_3_2) + #define _SOKOL_GL_HAS_COLORMASKI (1) + #define _SOKOL_GL_HAS_COPYIMAGE (1) + #define _SOKOL_GL_HAS_BASEVERTEX (1) + #endif #endif #endif @@ -7101,6 +7163,7 @@ typedef struct { uint32_t write_transient_frame_index; // last frame index when written to struct { uint32_t bind_frame_index; // last frame index the image was bound + uint32_t copy_src_frame_index; // last frame index the image was used as copy-source } validate; sg_image_type type; int width; @@ -7110,8 +7173,6 @@ typedef struct { sg_image_usage usage; sg_pixel_format pixel_format; int sample_count; - // deprecated: - uint32_t upd_frame_index; } _sg_image_common_t; typedef struct { @@ -7412,6 +7473,8 @@ typedef struct { bool valid; GLuint vao; // global mutated vertex-array-object GLuint fb; // global mutated framebuffer + GLuint copy_src_fb; // copy source framebuffer + GLuint copy_dst_fb; // copy destination framebuffer _sg_gl_cache_t cache; bool ext_anisotropic; GLint max_anisotropy; @@ -9064,6 +9127,7 @@ _SOKOL_PRIVATE void _sg_image_common_init(_sg_image_common_t* cmn, const sg_imag cmn->active_slot = 0; cmn->write_transient_frame_index = 0; cmn->validate.bind_frame_index = 0; + cmn->validate.copy_src_frame_index = 0; cmn->type = desc->type; cmn->width = desc->width; cmn->height = desc->height; @@ -9072,8 +9136,6 @@ _SOKOL_PRIVATE void _sg_image_common_init(_sg_image_common_t* cmn, const sg_imag cmn->usage = desc->usage; cmn->pixel_format = desc->pixel_format; cmn->sample_count = desc->sample_count; - // deprecated - cmn->upd_frame_index = 0; } _SOKOL_PRIVATE void _sg_sampler_common_init(_sg_sampler_common_t* cmn, const sg_sampler_desc* desc) { @@ -10168,7 +10230,8 @@ _SOKOL_PRIVATE void _sg_dummy_copy_buffer_to_image(_sg_buffer_t* src_buf, _sg_im _SG_XMACRO(glDrawElementsInstancedBaseVertex, void, (GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instancecount, GLint basevertex)) \ _SG_XMACRO(glDrawElementsInstancedBaseVertexBaseInstance, void, (GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance)) \ _SG_XMACRO(glDrawArraysInstancedBaseInstance, void, (GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance)) \ - _SG_XMACRO(glCopyBufferSubData, void, (GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size)) + _SG_XMACRO(glCopyBufferSubData, void, (GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size)) \ + _SG_XMACRO(glCopyImageSubData, void, (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth)) \ // generate GL function pointer typedefs #define _SG_XMACRO(name, ret, args) typedef ret (GL_APIENTRY* PFN_ ## name) args; @@ -10977,6 +11040,11 @@ _SOKOL_PRIVATE void _sg_gl_init_caps_glcore(void) { _sg.features.vertexformat_int10_n2 = true; _sg.features.copy_buffer_to_image_relaxed_buffer_type = true; _sg.features.copy_buffer_to_image_relaxed_bytes_per_row = true; + #if defined(_SOKOL_GL_HAS_COPYIMAGE) + _sg.features.copy_image_to_image_relaxed = version >= 430; + #else + _sg.features.copy_image_to_image_relaxed = false; + #endif // scan extensions bool has_s3tc = false; // BC1..BC3 @@ -11101,12 +11169,17 @@ _SOKOL_PRIVATE void _sg_gl_init_caps_gles3(void) { #else _sg.features.separate_buffer_types = false; #endif + #if defined(_SOKOL_GL_HAS_BASEVERTEX) _sg.features.draw_base_vertex = version >= 320; + #else + _sg.features.draw_base_vertex = false; + #endif _sg.features.draw_base_instance = false; _sg.features.dual_source_blending = false; _sg.features.vertexformat_int10_n2 = true; _sg.features.copy_buffer_to_image_relaxed_buffer_type = true; _sg.features.copy_buffer_to_image_relaxed_bytes_per_row = true; + _sg.features.copy_image_to_image_relaxed = false; bool has_s3tc = false; // BC1..BC3 bool has_rgtc = false; // BC4 and BC5 @@ -11589,8 +11662,10 @@ _SOKOL_PRIVATE void _sg_gl_setup_backend(const sg_desc* desc) { glBindVertexArray(_sg.gl.vao); _SG_GL_CHECK_ERROR(); - // create global framebuffer object which will be mutated as needed + // create global framebuffer objects which will be mutated as needed glGenFramebuffers(1, &_sg.gl.fb); + glGenFramebuffers(1, &_sg.gl.copy_src_fb); + glGenFramebuffers(1, &_sg.gl.copy_dst_fb); _SG_GL_CHECK_ERROR(); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); @@ -11606,6 +11681,12 @@ _SOKOL_PRIVATE void _sg_gl_discard_backend(void) { if (_sg.gl.fb) { glDeleteFramebuffers(1, &_sg.gl.fb); } + if (_sg.gl.copy_src_fb) { + glDeleteFramebuffers(1, &_sg.gl.copy_src_fb); + } + if (_sg.gl.copy_dst_fb) { + glDeleteFramebuffers(1, &_sg.gl.copy_dst_fb); + } if (_sg.gl.vao) { glDeleteVertexArrays(1, &_sg.gl.vao); } @@ -11930,26 +12011,28 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_image(_sg_image_t* img, const sg_ _sg_gl_cache_store_texture_sampler_binding(0); _sg_gl_cache_bind_texture_sampler(0, img->gl.target, img->gl.tex[slot], 0); _sg_gl_texstorage(img); + const uint8_t* data_ptr = (const uint8_t*)desc->data.ptr; for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++) { - const GLvoid* data_ptr = desc->data.mip_levels[mip_index].ptr; - const GLsizei data_size = (GLsizei)desc->data.mip_levels[mip_index].size; const int mip_width = _sg_miplevel_dim(img->cmn.width, mip_index); const int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index); - const int mip_depth = (SG_IMAGETYPE_3D == img->cmn.type) ? _sg_miplevel_dim(img->cmn.num_slices, mip_index) : img->cmn.num_slices; + const int mip_depth_or_slices = (SG_IMAGETYPE_3D == img->cmn.type) ? _sg_miplevel_dim(img->cmn.num_slices, mip_index) : img->cmn.num_slices; + const int surf_pitch = _sg_surface_pitch(img->cmn.pixel_format, mip_width, mip_height, 1); + const int mip_size = surf_pitch * mip_depth_or_slices; if (SG_IMAGETYPE_CUBE == img->cmn.type) { - const int surf_pitch = _sg_surface_pitch(img->cmn.pixel_format, mip_width, mip_height, 1); // NOTE: surf_ptr is allowed to be null here - const uint8_t* surf_ptr = (const uint8_t*) data_ptr; + const uint8_t* surf_ptr = data_ptr; for (int i = 0; i < 6; i++) { const GLenum gl_img_target = _sg_gl_cubeface_target(i); - _sg_gl_teximage(img, gl_img_target, mip_index, mip_width, mip_height, mip_depth, surf_ptr, surf_pitch); + _sg_gl_teximage(img, gl_img_target, mip_index, mip_width, mip_height, mip_depth_or_slices, surf_ptr, surf_pitch); if (data_ptr) { - SOKOL_ASSERT((6 * surf_pitch) <= data_size); surf_ptr += surf_pitch; } } } else { - _sg_gl_teximage(img, img->gl.target, mip_index, mip_width, mip_height, mip_depth, data_ptr, data_size); + _sg_gl_teximage(img, img->gl.target, mip_index, mip_width, mip_height, mip_depth_or_slices, data_ptr, mip_size); + } + if (data_ptr) { + data_ptr += mip_size; } } _sg_gl_cache_restore_texture_sampler_binding(0); @@ -12328,27 +12411,29 @@ _SOKOL_PRIVATE void _sg_gl_discard_pipeline(_sg_pipeline_t* pip) { _sg_gl_cache_invalidate_pipeline(pip); } -_SOKOL_PRIVATE void _sg_gl_fb_attach_texture(const _sg_view_t* view, GLenum gl_att_type) { - const _sg_image_t* img = _sg_image_ref_ptr(&view->cmn.img.ref); - const GLuint gl_tex = img->gl.tex[0]; +_SOKOL_PRIVATE void _sg_gl_fb_attach_image_slice(GLenum fb_target, GLenum gl_att_type, const _sg_image_t* img, int mip_level, int slice) { + const GLuint gl_tex = img->gl.tex[img->cmn.active_slot]; SOKOL_ASSERT(gl_tex); - const GLuint gl_target = img->gl.target; - SOKOL_ASSERT(gl_target); - const int mip_level = view->cmn.img.mip_level; - const int slice = view->cmn.img.slice; switch (img->cmn.type) { case SG_IMAGETYPE_2D: - glFramebufferTexture2D(GL_FRAMEBUFFER, gl_att_type, gl_target, gl_tex, mip_level); + glFramebufferTexture2D(fb_target, gl_att_type, img->gl.target, gl_tex, mip_level); break; case SG_IMAGETYPE_CUBE: - glFramebufferTexture2D(GL_FRAMEBUFFER, gl_att_type, _sg_gl_cubeface_target(slice), gl_tex, mip_level); + glFramebufferTexture2D(fb_target, gl_att_type, _sg_gl_cubeface_target(slice), gl_tex, mip_level); break; default: - glFramebufferTextureLayer(GL_FRAMEBUFFER, gl_att_type, gl_tex, mip_level, slice); + glFramebufferTextureLayer(fb_target, gl_att_type, gl_tex, mip_level, slice); break; } } +_SOKOL_PRIVATE void _sg_gl_fb_attach_texture(const _sg_view_t* view, GLenum gl_att_type) { + const _sg_image_t* img = _sg_image_ref_ptr(&view->cmn.img.ref); + const int mip_level = view->cmn.img.mip_level; + const int slice = view->cmn.img.slice; + _sg_gl_fb_attach_image_slice(GL_FRAMEBUFFER, gl_att_type, img, mip_level, slice); +} + _SOKOL_PRIVATE GLenum _sg_gl_depth_stencil_attachment_type(const _sg_image_t* ds_img) { if (_sg_is_depth_stencil_format(ds_img->cmn.pixel_format)) { return GL_DEPTH_STENCIL_ATTACHMENT; @@ -12740,6 +12825,7 @@ _SOKOL_PRIVATE void _sg_gl_end_render_pass(const _sg_attachments_ptrs_t* atts) { const _sg_view_t* rsv_view = atts->resolve_views[i]; if (rsv_view && rsv_view->gl.msaa_resolve_frame_buffer) { if (!fb_read_bound) { + glDisable(GL_SCISSOR_TEST); glBindFramebuffer(GL_READ_FRAMEBUFFER, _sg.gl.fb); fb_read_bound = true; } @@ -12752,6 +12838,9 @@ _SOKOL_PRIVATE void _sg_gl_end_render_pass(const _sg_attachments_ptrs_t* atts) { fb_draw_bound = true; } } + if (fb_read_bound) { + glEnable(GL_SCISSOR_TEST); + } // invalidate framebuffers _SOKOL_UNUSED(fb_draw_bound); @@ -13477,6 +13566,90 @@ _SOKOL_PRIVATE void _sg_gl_copy_buffer_to_image(_sg_buffer_t* src_buf, _sg_image _SG_GL_CHECK_ERROR(); } +_SOKOL_PRIVATE void _sg_gl_copy_image_to_image(_sg_image_t* src_img, _sg_image_t* dst_img, const sg_copy_image_to_image_desc* desc) { + SOKOL_ASSERT(src_img && dst_img && desc); + SOKOL_ASSERT(src_img->cmn.usage.copy_src); + SOKOL_ASSERT(dst_img->cmn.usage.copy_dst); + _SG_GL_CHECK_ERROR(); + + #if defined(_SOKOL_GL_HAS_COMPUTE) + if (src_img->cmn.usage.storage_image || dst_img->cmn.usage.storage_image) { + glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT|GL_FRAMEBUFFER_BARRIER_BIT); + } + #endif + + const int src_mip = desc->src.mip_level; + const int src_x0 = desc->src.x; + const int src_y0 = desc->src.y; + const int src_slice = desc->src.slice; + const int dst_mip = desc->dst.mip_level; + const int dst_x0 = desc->dst.x; + const int dst_y0 = desc->dst.y; + const int dst_slice = desc->dst.slice; + const int w = desc->size.width; + const int h = desc->size.height; + + // simple shortcut for GL 4.3+ + #if defined(_SOKOL_GL_HAS_COPYIMAGE) + if (_sg.features.copy_image_to_image_relaxed) { + const GLuint gl_src_tex = src_img->gl.tex[src_img->cmn.active_slot]; + const GLuint gl_dst_tex = dst_img->gl.tex[dst_img->cmn.active_slot]; + SOKOL_ASSERT(gl_src_tex && gl_dst_tex); + glCopyImageSubData( + gl_src_tex, + src_img->gl.target, + src_mip, + src_x0, + src_y0, + src_slice, + gl_dst_tex, + dst_img->gl.target, + dst_mip, + dst_x0, + dst_y0, + dst_slice, + w, + h, + desc->size.num_slices); + _SG_GL_CHECK_ERROR(); + return; + } + #endif + + // fallback path via glBlitFramebuffer for GLES3 and GL <4.3 + const sg_pixel_format fmt = src_img->cmn.pixel_format; + GLenum gl_att_type = GL_COLOR_ATTACHMENT0; + GLbitfield gl_mask = GL_COLOR_BUFFER_BIT; + if (_sg_is_depth_or_depth_stencil_format(fmt)) { + gl_att_type = _sg_gl_depth_stencil_attachment_type(src_img); + gl_mask = GL_DEPTH_BUFFER_BIT; + if (_sg_is_depth_stencil_format(fmt)) { + gl_mask |= GL_STENCIL_BUFFER_BIT; + } + } + glDisable(GL_SCISSOR_TEST); + #if defined(SOKOL_GLCORE) + glDisable(GL_FRAMEBUFFER_SRGB); + #endif + const int src_x1 = src_x0 + w; + const int src_y1 = src_y0 + h; + const int dst_x1 = dst_x0 + w; + const int dst_y1 = dst_y0 + h; + glBindFramebuffer(GL_READ_FRAMEBUFFER, _sg.gl.copy_src_fb); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _sg.gl.copy_dst_fb); + for (int i = 0; i < desc->size.num_slices; i++) { + _sg_gl_fb_attach_image_slice(GL_READ_FRAMEBUFFER, gl_att_type, src_img, src_mip, src_slice + i); + _sg_gl_fb_attach_image_slice(GL_DRAW_FRAMEBUFFER, gl_att_type, dst_img, dst_mip, dst_slice + i); + glBlitFramebuffer(src_x0, src_y0, src_x1, src_y1, dst_x0, dst_y0, dst_x1, dst_y1, gl_mask, GL_NEAREST); + } + // detach, otherwise some drivers fail on a later color- vs depth-attachment switch + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, gl_att_type, GL_TEXTURE_2D, 0, 0); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, gl_att_type, GL_TEXTURE_2D, 0, 0); + // NOTE: draw-fb and GL_FRAMEBUFFER_SRGB are set as needed in next sg_begin_pass() + glEnable(GL_SCISSOR_TEST); + _SG_GL_CHECK_ERROR(); +} + // ██████ ██████ ██████ ██ ██ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████ // ██ ██ ██ ██ ██ ███ ███ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ // ██ ██ █████ ██ ██ ██ ██ ██████ ███████ ██ █████ █████ ██ ██ ██ ██ ██ @@ -14372,6 +14545,7 @@ _SOKOL_PRIVATE void _sg_d3d11_init_caps(void) { _sg.features.copy_buffer_to_image_relaxed_buffer_type = false; // technically irrelevant since the bytes-per-row alignment rule only affects non-staging buffers _sg.features.copy_buffer_to_image_relaxed_bytes_per_row = true; + _sg.features.copy_image_to_image_relaxed = true; _sg.limits.max_image_size_2d = 16 * 1024; _sg.limits.max_image_size_cube = 16 * 1024; @@ -14489,28 +14663,37 @@ _SOKOL_PRIVATE void _sg_d3d11_seal_buffer(_sg_buffer_t* buf) { _SOKOL_UNUSED(buf); } -_SOKOL_PRIVATE void _sg_d3d11_fill_subres_data(const _sg_image_t* img, const sg_image_data* data) { - const int num_slices = (img->cmn.type == SG_IMAGETYPE_3D) ? 1 : img->cmn.num_slices; - int subres_index = 0; - for (int slice_index = 0; slice_index < num_slices; slice_index++) { - for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++, subres_index++) { +// D3D11CalcSubresource only exists for C++ +_SOKOL_PRIVATE UINT _sg_d3d11_calcsubresource(UINT mip_slice, UINT array_slice, UINT mip_levels) { + return mip_slice + array_slice * mip_levels; +} + +_SOKOL_PRIVATE void _sg_d3d11_fill_image_subres_data(const _sg_image_t* img, const sg_range* data) { + SOKOL_ASSERT(data && data->ptr && (data->size > 0)); + const uint8_t* ptr = (const uint8_t*)data->ptr; + const int num_array_slices = (img->cmn.type == SG_IMAGETYPE_3D) ? 1 : img->cmn.num_slices; + size_t mip_offset = 0; + for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++) { + const int mip_width = _sg_miplevel_dim(img->cmn.width, mip_index); + const int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index); + const int mip_depth_or_slices = (SG_IMAGETYPE_3D == img->cmn.type) ? _sg_miplevel_dim(img->cmn.num_slices, mip_index) : img->cmn.num_slices; + const int row_pitch = _sg_row_pitch(img->cmn.pixel_format, mip_width, 1); + const int surf_pitch = _sg_surface_pitch(img->cmn.pixel_format, mip_width, mip_height, 1); + for (int array_index = 0; array_index < num_array_slices; array_index++) { + const UINT subres_index = _sg_d3d11_calcsubresource((UINT)mip_index, (UINT)array_index, (UINT)img->cmn.num_mipmaps); SOKOL_ASSERT(subres_index < _SG_D3D11_MAX_TEXTURE_SUBRESOURCES); D3D11_SUBRESOURCE_DATA* subres_data = &_sg.d3d11.subres_data[subres_index]; - const int mip_width = _sg_miplevel_dim(img->cmn.width, mip_index); - const int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index); - const sg_range* miplevel_data = &(data->mip_levels[mip_index]); - const size_t slice_size = miplevel_data->size / (size_t)num_slices; - const size_t slice_offset = slice_size * (size_t)slice_index; - const uint8_t* ptr = (const uint8_t*) miplevel_data->ptr; - subres_data->pSysMem = ptr + slice_offset; - subres_data->SysMemPitch = (UINT)_sg_row_pitch(img->cmn.pixel_format, mip_width, 1); + subres_data->pSysMem = ptr + mip_offset + (size_t)array_index * (size_t)surf_pitch; + subres_data->SysMemPitch = (UINT)row_pitch; if (img->cmn.type == SG_IMAGETYPE_3D) { - subres_data->SysMemSlicePitch = (UINT)_sg_surface_pitch(img->cmn.pixel_format, mip_width, mip_height, 1); + subres_data->SysMemSlicePitch = (UINT)surf_pitch; } else { subres_data->SysMemSlicePitch = 0; } } + mip_offset += (size_t)surf_pitch * (size_t)mip_depth_or_slices; } + SOKOL_ASSERT(mip_offset == data->size); } _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const sg_image_desc* desc) { @@ -14529,8 +14712,8 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const // prepare initial content pointers D3D11_SUBRESOURCE_DATA* init_data = 0; - if (!injected && desc->data.mip_levels[0].ptr) { - _sg_d3d11_fill_subres_data(img, &desc->data); + if (!injected && desc->data.ptr) { + _sg_d3d11_fill_image_subres_data(img, &desc->data); init_data = _sg.d3d11.subres_data; } if (img->cmn.type != SG_IMAGETYPE_3D) { @@ -15426,11 +15609,6 @@ _SOKOL_PRIVATE void _sg_d3d11_begin_pass(const sg_pass* pass, const _sg_attachme } } -// D3D11CalcSubresource only exists for C++ -_SOKOL_PRIVATE UINT _sg_d3d11_calcsubresource(UINT mip_slice, UINT array_slice, UINT mip_levels) { - return mip_slice + array_slice * mip_levels; -} - _SOKOL_PRIVATE void _sg_d3d11_end_pass(const _sg_attachments_ptrs_t* atts) { SOKOL_ASSERT(_sg.d3d11.ctx && atts); @@ -15807,15 +15985,16 @@ _SOKOL_PRIVATE void _sg_d3d11_write_miplevel_data(const _sg_image_t* img, SOKOL_ASSERT((src_offset + _sg_image_copy_size(img->cmn.pixel_format, src_bytes_per_row, src_bytes_per_slice, width, height, num_slices)) <= src_size); _SOKOL_UNUSED(src_size); + const int block_dim = _sg_block_dim(img->cmn.pixel_format); const UINT d3d11_src_row_pitch = (UINT)src_bytes_per_row; UINT d3d11_src_depth_pitch = 0; int d3d11_slice = 0; int d3d11_num_slices = 0; _SG_STRUCT(D3D11_BOX, d3d11_dst_box); d3d11_dst_box.left = (UINT)x; - d3d11_dst_box.right = (UINT)(x + width); + d3d11_dst_box.right = (UINT)_sg_roundup_pow2(x + width, block_dim); d3d11_dst_box.top = (UINT)y; - d3d11_dst_box.bottom = (UINT)(y + height); + d3d11_dst_box.bottom = (UINT)_sg_roundup_pow2(y + height, block_dim); if (img->cmn.type == SG_IMAGETYPE_3D) { d3d11_slice = 0; d3d11_num_slices = 1; @@ -15830,7 +16009,7 @@ _SOKOL_PRIVATE void _sg_d3d11_write_miplevel_data(const _sg_image_t* img, d3d11_src_depth_pitch = 0; } for (int i = 0; i < d3d11_num_slices; i++) { - const void* d3d11_src_ptr = (void*)(src_ptr + src_offset + i * src_bytes_per_slice); + const void* d3d11_src_ptr = (const void*)(src_ptr + src_offset + (size_t)i * (size_t)src_bytes_per_slice); const UINT d3d11_subres = _sg_d3d11_calcsubresource((UINT)mip_level, (UINT)(d3d11_slice + i), (UINT)img->cmn.num_mipmaps); _sg_d3d11_UpdateSubresource(_sg.d3d11.ctx, d3d11_res, @@ -15999,6 +16178,64 @@ _SOKOL_PRIVATE void _sg_d3d11_copy_buffer_to_image(_sg_buffer_t* src_buf, _sg_im desc->size.num_slices); } +_SOKOL_PRIVATE void _sg_d3d11_copy_image_to_image(_sg_image_t* src_img, _sg_image_t* dst_img, const sg_copy_image_to_image_desc* desc) { + SOKOL_ASSERT(_sg.d3d11.ctx); + SOKOL_ASSERT(src_img && dst_img && desc); + SOKOL_ASSERT(src_img->cmn.usage.copy_src); + SOKOL_ASSERT(dst_img->cmn.usage.copy_dst); + SOKOL_ASSERT(src_img->d3d11.res); + SOKOL_ASSERT(dst_img->d3d11.res); + + // NOTE: msaa and depth-stencil images can only be copied as whole mip levels + const int block_dim = _sg_block_dim(src_img->cmn.pixel_format); + const bool is_depth_stencil = _sg_is_depth_or_depth_stencil_format(src_img->cmn.pixel_format); + const bool is_msaa = src_img->cmn.sample_count > 1; + const bool whole_subres = is_depth_stencil || is_msaa; + _SG_STRUCT(D3D11_BOX, d3d11_src_box); + d3d11_src_box.left = (UINT)desc->src.x; + d3d11_src_box.right = (UINT)_sg_roundup_pow2(desc->src.x + desc->size.width, block_dim); + d3d11_src_box.top = (UINT)desc->src.y; + d3d11_src_box.bottom = (UINT)_sg_roundup_pow2(desc->src.y + desc->size.height, block_dim); + UINT d3d11_dst_z = 0; + int d3d11_src_slice = 0; + int d3d11_dst_slice = 0; + int d3d11_num_slices = 0; + if (src_img->cmn.type == SG_IMAGETYPE_3D) { + SOKOL_ASSERT(dst_img->cmn.type == SG_IMAGETYPE_3D); + // 3D images cannot be multi-sampled or depth-stencil in the first place + SOKOL_ASSERT(!whole_subres); + d3d11_src_box.front = (UINT)desc->src.slice; + d3d11_src_box.back = (UINT)(desc->src.slice + desc->size.num_slices); + d3d11_dst_z = (UINT)desc->dst.slice; + d3d11_num_slices = 1; + } else { + SOKOL_ASSERT(dst_img->cmn.type != SG_IMAGETYPE_3D); + d3d11_src_box.front = 0; + d3d11_src_box.back = 1; + d3d11_src_slice = desc->src.slice; + d3d11_dst_slice = desc->dst.slice; + d3d11_num_slices = desc->size.num_slices; + } + const UINT src_mip_level = (UINT)desc->src.mip_level; + const UINT src_num_mips = (UINT)src_img->cmn.num_mipmaps; + const UINT dst_mip_level = (UINT)desc->dst.mip_level; + const UINT dst_num_mips = (UINT)dst_img->cmn.num_mipmaps; + for (int i = 0; i < d3d11_num_slices; i++) { + const UINT src_array_index = (UINT)(d3d11_src_slice + i); + const UINT dst_array_index = (UINT)(d3d11_dst_slice + i); + const UINT d3d11_src_subres = _sg_d3d11_calcsubresource(src_mip_level, src_array_index, src_num_mips); + const UINT d3d11_dst_subres = _sg_d3d11_calcsubresource(dst_mip_level, dst_array_index, dst_num_mips); + _sg_d3d11_CopySubresourceRegion(_sg.d3d11.ctx, + dst_img->d3d11.res, // pDstResource + d3d11_dst_subres, // DstSubresource + (UINT)desc->dst.x, // DstX + (UINT)desc->dst.y, // DstY + d3d11_dst_z, // DstZ + src_img->d3d11.res, // pSrcResource + d3d11_src_subres, // SrcSubresource + whole_subres ? 0 : &d3d11_src_box); // pSrcBox (0 for whole-mip copies) + } +} // ███ ███ ███████ ████████ █████ ██ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████ // ████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ @@ -16537,6 +16774,7 @@ _SOKOL_PRIVATE void _sg_mtl_init_caps(void) { _sg.features.vertexformat_int10_n2 = true; _sg.features.copy_buffer_to_image_relaxed_buffer_type = true; _sg.features.copy_buffer_to_image_relaxed_bytes_per_row = true; + _sg.features.copy_image_to_image_relaxed = true; _sg.features.image_clamp_to_border = false; #if (MAC_OS_X_VERSION_MAX_ALLOWED >= 120000) || (__IPHONE_OS_VERSION_MAX_ALLOWED >= 140000) @@ -16932,19 +17170,19 @@ _SOKOL_PRIVATE void _sg_mtl_write_miplevel_data(const _sg_image_t* img, } } -_SOKOL_PRIVATE void _sg_mtl_copy_image_data(const _sg_image_t* img, __unsafe_unretained id mtl_tex, const sg_image_data* data) { +_SOKOL_PRIVATE void _sg_mtl_copy_image_data(const _sg_image_t* img, __unsafe_unretained id mtl_tex, const sg_range* data) { + SOKOL_ASSERT(data && data->ptr && (data->size > 0)); + size_t src_offset = 0; for (int mip_level = 0; mip_level < img->cmn.num_mipmaps; mip_level++) { - SOKOL_ASSERT(data->mip_levels[mip_level].ptr); - SOKOL_ASSERT(data->mip_levels[mip_level].size > 0); const int mip_width = _sg_miplevel_dim(img->cmn.width, mip_level); const int mip_height = _sg_miplevel_dim(img->cmn.height, mip_level); const int mip_depth_or_slices = (SG_IMAGETYPE_3D == img->cmn.type) ? _sg_miplevel_dim(img->cmn.num_slices, mip_level) : img->cmn.num_slices; const int bytes_per_row = _sg_row_pitch(img->cmn.pixel_format, mip_width, 1); const int bytes_per_slice = _sg_surface_pitch(img->cmn.pixel_format, mip_width, mip_height, 1); _sg_mtl_write_miplevel_data(img, mtl_tex, - (const uint8_t*)data->mip_levels[mip_level].ptr, - data->mip_levels[mip_level].size, - 0, // src_offset + (const uint8_t*)data->ptr, + data->size, + src_offset, bytes_per_row, bytes_per_slice, mip_level, @@ -16954,6 +17192,7 @@ _SOKOL_PRIVATE void _sg_mtl_copy_image_data(const _sg_image_t* img, __unsafe_unr mip_width, mip_height, mip_depth_or_slices); + src_offset += (size_t)bytes_per_slice * (size_t)mip_depth_or_slices; } } @@ -17029,7 +17268,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_image(_sg_image_t* img, const sg _SG_ERROR(METAL_CREATE_TEXTURE_FAILED); return SG_RESOURCESTATE_FAILED; } - if (desc->data.mip_levels[0].ptr) { + if (desc->data.ptr) { _sg_mtl_copy_image_data(img, mtl_tex, &desc->data); } } @@ -18398,6 +18637,49 @@ _SOKOL_PRIVATE void _sg_mtl_copy_buffer_to_image(_sg_buffer_t* src_buf, _sg_imag } } +_SOKOL_PRIVATE void _sg_mtl_copy_image_to_image(_sg_image_t* src_img, _sg_image_t* dst_img, const sg_copy_image_to_image_desc* desc) { + SOKOL_ASSERT(src_img && dst_img && desc); + SOKOL_ASSERT(src_img->cmn.usage.copy_src); + SOKOL_ASSERT(dst_img->cmn.usage.copy_dst); + + _sg_mtl_acquire_cmd_buffer(); + SOKOL_ASSERT(_sg.mtl.cmd_buffer); + _sg_mtl_acquire_blit_cmd_encoder(); + SOKOL_ASSERT(_sg.mtl.blit_cmd_encoder); + + __unsafe_unretained id mtl_src_tex = _sg_mtl_id(src_img->mtl.tex[src_img->cmn.active_slot]); + __unsafe_unretained id mtl_dst_tex = _sg_mtl_id(dst_img->mtl.tex[dst_img->cmn.active_slot]); + + MTLSize mtl_size; + MTLOrigin mtl_src_origin; + MTLOrigin mtl_dst_origin; + if (src_img->cmn.type == SG_IMAGETYPE_3D) { + SOKOL_ASSERT(dst_img->cmn.type == SG_IMAGETYPE_3D); + mtl_size = MTLSizeMake((NSUInteger)desc->size.width, (NSUInteger)desc->size.height, (NSUInteger)desc->size.num_slices); + mtl_src_origin = MTLOriginMake((NSUInteger)desc->src.x, (NSUInteger)desc->src.y, (NSUInteger)desc->src.slice); + mtl_dst_origin = MTLOriginMake((NSUInteger)desc->dst.x, (NSUInteger)desc->dst.y, (NSUInteger)desc->dst.slice); + } else { + SOKOL_ASSERT(dst_img->cmn.type != SG_IMAGETYPE_3D); + mtl_size = MTLSizeMake((NSUInteger)desc->size.width, (NSUInteger)desc->size.height, 1); + mtl_src_origin = MTLOriginMake((NSUInteger)desc->src.x, (NSUInteger)desc->src.y, 0); + mtl_dst_origin = MTLOriginMake((NSUInteger)desc->dst.x, (NSUInteger)desc->dst.y, 0); + } + const int mtl_src_slice_index = (src_img->cmn.type == SG_IMAGETYPE_3D) ? 0 : desc->src.slice; + const int mtl_dst_slice_index = (dst_img->cmn.type == SG_IMAGETYPE_3D) ? 0 : desc->dst.slice; + const int mtl_num_slices = (dst_img->cmn.type == SG_IMAGETYPE_3D) ? 1 : desc->size.num_slices; + for (int i = 0; i < mtl_num_slices; i++) { + [_sg.mtl.blit_cmd_encoder copyFromTexture:mtl_src_tex + sourceSlice:(NSUInteger)(mtl_src_slice_index + i) + sourceLevel:(NSUInteger)desc->src.mip_level + sourceOrigin:mtl_src_origin + sourceSize:mtl_size + toTexture:mtl_dst_tex + destinationSlice:(NSUInteger)(mtl_dst_slice_index + i) + destinationLevel:(NSUInteger)desc->dst.mip_level + destinationOrigin:mtl_dst_origin]; + } +} + _SOKOL_PRIVATE void _sg_mtl_push_debug_group(const char* name) { SOKOL_ASSERT(name); if (_sg.mtl.render_cmd_encoder) { @@ -18851,6 +19133,7 @@ _SOKOL_PRIVATE void _sg_wgpu_init_caps(void) { _sg.features.copy_buffer_to_image_relaxed_buffer_type = true; // when copying from non-staging buffer, bytes-per-row must be multiple of 256 _sg.features.copy_buffer_to_image_relaxed_bytes_per_row = false; + _sg.features.copy_image_to_image_relaxed = true; wgpuDeviceGetLimits(_sg.wgpu.dev, &_sg.wgpu.limits); @@ -19758,7 +20041,9 @@ _SOKOL_PRIVATE void _sg_wgpu_write_miplevel_data(const _sg_image_t* img, wgpuQueueWriteTexture(_sg.wgpu.queue, &wgpu_copy_tex, (const void*)src_ptr, src_size, &wgpu_layout, &wgpu_extent); } -_SOKOL_PRIVATE void _sg_wgpu_copy_image_data(const _sg_image_t* img, const sg_image_data* data) { +_SOKOL_PRIVATE void _sg_wgpu_copy_image_data(const _sg_image_t* img, const sg_range* data) { + SOKOL_ASSERT(data && data->ptr && (data->size > 0)); + size_t src_offset = 0; for (int mip_level = 0; mip_level < img->cmn.num_mipmaps; mip_level++) { const int mip_width = _sg_miplevel_dim(img->cmn.width, mip_level); const int mip_height = _sg_miplevel_dim(img->cmn.height, mip_level); @@ -19766,9 +20051,9 @@ _SOKOL_PRIVATE void _sg_wgpu_copy_image_data(const _sg_image_t* img, const sg_im const int bytes_per_row = _sg_row_pitch(img->cmn.pixel_format, mip_width, 1); const int bytes_per_slice = _sg_surface_pitch(img->cmn.pixel_format, mip_width, mip_height, 1); _sg_wgpu_write_miplevel_data(img, - (const uint8_t*)data->mip_levels[mip_level].ptr, - data->mip_levels[mip_level].size, - 0, // src_offset + (const uint8_t*)data->ptr, + data->size, + src_offset, bytes_per_row, bytes_per_slice, mip_level, @@ -19778,6 +20063,7 @@ _SOKOL_PRIVATE void _sg_wgpu_copy_image_data(const _sg_image_t* img, const sg_im mip_width, mip_height, mip_depth_or_slices); + src_offset += (size_t)bytes_per_slice * (size_t)mip_depth_or_slices; } } @@ -19799,6 +20085,12 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_image(_sg_image_t* img, const s if (desc->usage.storage_image) { wgpu_tex_desc.usage |= WGPUTextureUsage_StorageBinding; } + if (desc->usage.copy_src) { + wgpu_tex_desc.usage |= WGPUTextureUsage_CopySrc; + } + if (desc->usage.copy_dst) { + wgpu_tex_desc.usage |= WGPUTextureUsage_CopyDst; + } wgpu_tex_desc.dimension = _sg_wgpu_texture_dimension(img->cmn.type); wgpu_tex_desc.size.width = (uint32_t) img->cmn.width; wgpu_tex_desc.size.height = (uint32_t) img->cmn.height; @@ -19811,7 +20103,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_image(_sg_image_t* img, const s _SG_ERROR(WGPU_CREATE_TEXTURE_FAILED); return SG_RESOURCESTATE_FAILED; } - if (desc->data.mip_levels[0].ptr) { + if (desc->data.ptr) { _sg_wgpu_copy_image_data(img, &desc->data); } } @@ -20683,6 +20975,36 @@ _SOKOL_PRIVATE void _sg_wgpu_copy_buffer_to_image(_sg_buffer_t* src_buf, _sg_ima } } +_SOKOL_PRIVATE void _sg_wgpu_copy_image_to_image(_sg_image_t* src_img, _sg_image_t* dst_img, const sg_copy_image_to_image_desc* desc) { + SOKOL_ASSERT(src_img && dst_img && desc); + SOKOL_ASSERT(src_img->cmn.usage.copy_src); + SOKOL_ASSERT(dst_img->cmn.usage.copy_dst); + SOKOL_ASSERT(src_img->wgpu.tex); + SOKOL_ASSERT(dst_img->wgpu.tex); + + _sg_wgpu_acquire_command_encoder(); + const int block_dim = _sg_block_dim(src_img->cmn.pixel_format); + _SG_STRUCT(WGPUTexelCopyTextureInfo, wgpu_src_info); + _SG_STRUCT(WGPUTexelCopyTextureInfo, wgpu_dst_info); + _SG_STRUCT(WGPUExtent3D, wgpu_copy_size); + wgpu_src_info.texture = src_img->wgpu.tex; + wgpu_src_info.mipLevel = (uint32_t)desc->src.mip_level; + wgpu_src_info.origin.x = (uint32_t)desc->src.x; + wgpu_src_info.origin.y = (uint32_t)desc->src.y; + wgpu_src_info.origin.z = (uint32_t)desc->src.slice; + wgpu_src_info.aspect = WGPUTextureAspect_All; + wgpu_dst_info.texture = dst_img->wgpu.tex; + wgpu_dst_info.mipLevel = (uint32_t)desc->dst.mip_level; + wgpu_dst_info.origin.x = (uint32_t)desc->dst.x; + wgpu_dst_info.origin.y = (uint32_t)desc->dst.y; + wgpu_dst_info.origin.z = (uint32_t)desc->dst.slice; + wgpu_dst_info.aspect = WGPUTextureAspect_All; + wgpu_copy_size.width = (uint32_t)_sg_roundup_pow2(desc->size.width, block_dim); + wgpu_copy_size.height = (uint32_t)_sg_roundup_pow2(desc->size.height, block_dim); + wgpu_copy_size.depthOrArrayLayers = (uint32_t)desc->size.num_slices; + wgpuCommandEncoderCopyTextureToTexture(_sg.wgpu.cmd_enc, &wgpu_src_info, &wgpu_dst_info, &wgpu_copy_size); +} + // ██ ██ ██ ██ ██ ██ ██ █████ ███ ██ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████ // ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ // ██ ██ ██ ██ ██ █████ ███████ ██ ██ ██ ██████ ███████ ██ █████ █████ ██ ██ ██ ██ ██ @@ -20965,7 +21287,9 @@ _SOKOL_PRIVATE void _sg_vk_swapchain_endpass_barrier(VkCommandBuffer cmd_buf, Vk _SOKOL_PRIVATE void _sg_vk_image_barrier(VkCommandBuffer cmd_buf, _sg_image_t* img, _sg_vk_access_t new_access) { SOKOL_ASSERT(cmd_buf && img && img->vk.img); - if (_sg_vk_is_read_access(img->vk.cur_access) && _sg_vk_is_read_access(new_access)) { + const bool all_read_access = _sg_vk_is_read_access(img->vk.cur_access) && _sg_vk_is_read_access(new_access); + const bool no_layout_change = _sg_vk_image_layout(img->vk.cur_access) == _sg_vk_image_layout(new_access); + if (all_read_access && no_layout_change) { return; } _SG_STRUCT(VkImageMemoryBarrier2, barrier); @@ -21743,14 +22067,10 @@ _SOKOL_PRIVATE void _sg_vk_init_vk_image_staging_structs(const _sg_image_t* img, SOKOL_ASSERT(img && region && copy_info); region->sType = VK_STRUCTURE_TYPE_BUFFER_IMAGE_COPY_2; - if (_sg_is_depth_or_depth_stencil_format(img->cmn.pixel_format)) { - region->imageSubresource.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; - if (_sg_is_depth_stencil_format(img->cmn.pixel_format)) { - region->imageSubresource.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT; - } - } else { - region->imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - } + // NOTE: trying to stage into depth or depth-stencil buffers is actually illegal + // (also checked by validation layer) + SOKOL_ASSERT(!_sg_is_depth_or_depth_stencil_format(img->cmn.pixel_format)); + region->imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; region->imageSubresource.layerCount = 1; region->imageExtent.depth = 1; @@ -21862,21 +22182,21 @@ _SOKOL_PRIVATE void _sg_vk_staging_copy_miplevel_data(_sg_image_t* img, } } -_SOKOL_PRIVATE void _sg_vk_staging_copy_image_data(_sg_image_t* img, const sg_image_data* src_data, bool initial_wait) { +_SOKOL_PRIVATE void _sg_vk_staging_copy_image_data(_sg_image_t* img, const sg_range* src_data, bool initial_wait) { SOKOL_ASSERT(img); + SOKOL_ASSERT(src_data && src_data->ptr && (src_data->size > 0)); const sg_pixel_format fmt = img->cmn.pixel_format; + size_t src_offset = 0; for (int mip_level = 0; mip_level < img->cmn.num_mipmaps; mip_level++) { - SOKOL_ASSERT(src_data->mip_levels[mip_level].ptr); - SOKOL_ASSERT(src_data->mip_levels[mip_level].size > 0); const int mip_width = _sg_miplevel_dim(img->cmn.width, mip_level); const int mip_height = _sg_miplevel_dim(img->cmn.height, mip_level); const int mip_depth_or_slices = (img->cmn.type == SG_IMAGETYPE_3D) ? _sg_miplevel_dim(img->cmn.num_slices, mip_level) : img->cmn.num_slices; const int bytes_per_row = _sg_row_pitch(fmt, mip_width, 1); const int bytes_per_slice = _sg_surface_pitch(fmt, mip_width, mip_height, 1); _sg_vk_staging_copy_miplevel_data(img, - (const uint8_t*)src_data->mip_levels[mip_level].ptr, - src_data->mip_levels[mip_level].size, - 0, // src_offset + (const uint8_t*)src_data->ptr, + src_data->size, + src_offset, bytes_per_row, bytes_per_slice, mip_level, @@ -21887,7 +22207,9 @@ _SOKOL_PRIVATE void _sg_vk_staging_copy_image_data(_sg_image_t* img, const sg_im mip_height, mip_depth_or_slices, (mip_level == 0) ? initial_wait : false); + src_offset += (size_t)bytes_per_slice * (size_t)mip_depth_or_slices; } + SOKOL_ASSERT(src_offset == src_data->size); } // staging system for non-blocking streaming (aka transient) updates with a max per-frame data limit @@ -22327,6 +22649,9 @@ _SOKOL_PRIVATE VkImageUsageFlags _sg_vk_image_usage(const sg_image_usage* usg) { if (usg->depth_stencil_attachment) { res |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; } + if (usg->copy_src) { + res |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + } return res; } @@ -22653,6 +22978,7 @@ _SOKOL_PRIVATE void _sg_vk_init_caps(void) { _sg.features.vertexformat_int10_n2 = true; _sg.features.copy_buffer_to_image_relaxed_buffer_type = true; _sg.features.copy_buffer_to_image_relaxed_bytes_per_row = true; + _sg.features.copy_image_to_image_relaxed = true; SOKOL_ASSERT(_sg.vk.phys_dev); _sg.vk.descriptor_buffer_props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_PROPERTIES_EXT; @@ -23031,7 +23357,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_vk_create_image(_sg_image_t* img, const sg_ _SG_ERROR(VULKAN_BIND_IMAGE_MEMORY_FAILED); return SG_RESOURCESTATE_FAILED; } - if (desc->data.mip_levels[0].ptr) { + if (desc->data.ptr) { _sg_vk_staging_copy_image_data(img, &desc->data, false); } return SG_RESOURCESTATE_VALID; @@ -24122,6 +24448,73 @@ _SOKOL_PRIVATE void _sg_vk_copy_buffer_to_image(_sg_buffer_t* src_buf, _sg_image _sg_vk_buffer_barrier(cmd_buf, src_buf, _sg_vk_default_buffer_access_mask(src_buf)); _sg_vk_image_barrier(cmd_buf, dst_img, _sg_vk_default_image_access_mask(dst_img)); } + +_SOKOL_PRIVATE void _sg_vk_copy_image_to_image(_sg_image_t* src_img, _sg_image_t* dst_img, const sg_copy_image_to_image_desc* desc) { + SOKOL_ASSERT(src_img && dst_img && desc); + SOKOL_ASSERT(src_img->cmn.usage.copy_src); + SOKOL_ASSERT(dst_img->cmn.usage.copy_dst); + SOKOL_ASSERT(src_img->vk.img); + SOKOL_ASSERT(dst_img->vk.img); + + _sg_vk_acquire_frame_command_buffers(); + + // NOTE: not stream_cmd_buf but the regular cmd_buf, because gpu-copies must run + // interleaved with passes, while the stream_cmd_buf is submitted before cmd_buf + VkCommandBuffer cmd_buf = _sg.vk.frame.cmd_buf; + _sg_vk_image_barrier(cmd_buf, src_img, _SG_VK_ACCESS_COPY_SRC); + _sg_vk_image_barrier(cmd_buf, dst_img, _SG_VK_ACCESS_COPY_DST); + + const sg_pixel_format fmt = src_img->cmn.pixel_format; + VkImageAspectFlags aspect_mask = 0; + if (_sg_is_depth_or_depth_stencil_format(fmt)) { + aspect_mask = VK_IMAGE_ASPECT_DEPTH_BIT; + if (_sg_is_depth_stencil_format(fmt)) { + aspect_mask |= VK_IMAGE_ASPECT_STENCIL_BIT; + } + } else { + aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT; + } + _SG_STRUCT(VkImageCopy2, region); + region.sType = VK_STRUCTURE_TYPE_IMAGE_COPY_2; + region.srcSubresource.aspectMask = aspect_mask; + region.srcSubresource.mipLevel = (uint32_t)desc->src.mip_level; + region.srcOffset.x = desc->src.x; + region.srcOffset.y = desc->src.y; + region.dstSubresource.aspectMask = aspect_mask; + region.dstSubresource.mipLevel = (uint32_t)desc->dst.mip_level; + region.dstOffset.x = desc->dst.x; + region.dstOffset.y = desc->dst.y; + region.extent.width = (uint32_t)desc->size.width; + region.extent.height = (uint32_t)desc->size.height; + if (src_img->cmn.type == SG_IMAGETYPE_3D) { + SOKOL_ASSERT(dst_img->cmn.type == SG_IMAGETYPE_3D); + region.srcSubresource.layerCount = 1; + region.dstSubresource.layerCount = 1; + region.srcOffset.z = desc->src.slice; + region.dstOffset.z = desc->dst.slice; + region.extent.depth = (uint32_t)desc->size.num_slices; + } else { + SOKOL_ASSERT(dst_img->cmn.type != SG_IMAGETYPE_3D); + region.srcSubresource.baseArrayLayer = (uint32_t)desc->src.slice; + region.srcSubresource.layerCount = (uint32_t)desc->size.num_slices; + region.dstSubresource.baseArrayLayer = (uint32_t)desc->dst.slice; + region.dstSubresource.layerCount = (uint32_t)desc->size.num_slices; + region.extent.depth = 1; + } + _SG_STRUCT(VkCopyImageInfo2, copy_info); + copy_info.sType = VK_STRUCTURE_TYPE_COPY_IMAGE_INFO_2; + copy_info.srcImage = src_img->vk.img; + copy_info.srcImageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + copy_info.dstImage = dst_img->vk.img; + copy_info.dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + copy_info.regionCount = 1; + copy_info.pRegions = ®ion; + vkCmdCopyImage2(cmd_buf, ©_info); + _sg_stats_inc(vk.num_cmd_copy_image); + _sg_vk_image_barrier(cmd_buf, src_img, _sg_vk_default_image_access_mask(src_img)); + _sg_vk_image_barrier(cmd_buf, dst_img, _sg_vk_default_image_access_mask(dst_img)); +} + #endif // ██████ ███████ ███ ██ ███████ ██████ ██ ██████ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████ @@ -24725,6 +25118,23 @@ static inline void _sg_copy_buffer_to_image(_sg_buffer_t* src_buf, _sg_image_t* #endif } +static inline void _sg_copy_image_to_image(_sg_image_t* src_img, _sg_image_t* dst_img, const sg_copy_image_to_image_desc* desc) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_copy_image_to_image(src_img, dst_img, desc); + #elif defined(SOKOL_METAL) + _sg_mtl_copy_image_to_image(src_img, dst_img, desc); + #elif defined(SOKOL_D3D11) + _sg_d3d11_copy_image_to_image(src_img, dst_img, desc); + #elif defined(SOKOL_WGPU) + _sg_wgpu_copy_image_to_image(src_img, dst_img, desc); + #elif defined(SOKOL_VULKAN) + _sg_vk_copy_image_to_image(src_img, dst_img, desc); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_copy_image_to_image(src_img, dst_img, desc); + #else + #error("INVALID BACKEND"); + #endif +} static inline void _sg_push_debug_group(const char* name) { #if defined(SOKOL_METAL) _sg_mtl_push_debug_group(name); @@ -24840,28 +25250,6 @@ _SOKOL_PRIVATE bool _sg_validate_buffer_desc(const sg_buffer_desc* desc) { #endif } -_SOKOL_PRIVATE void _sg_validate_image_data(const sg_image_data* data, sg_pixel_format fmt, int width, int height, int num_mips, int num_slices) { - #if !defined(SOKOL_DEBUG) - _SOKOL_UNUSED(data); - _SOKOL_UNUSED(fmt); - _SOKOL_UNUSED(width); - _SOKOL_UNUSED(height); - _SOKOL_UNUSED(num_mips); - _SOKOL_UNUSED(num_slices); - #else - for (int mip_index = 0; mip_index < num_mips; mip_index++) { - const bool has_data = data->mip_levels[mip_index].ptr != 0; - const bool has_size = data->mip_levels[mip_index].size > 0; - _SG_VALIDATE(has_data && has_size, VALIDATE_IMAGEDATA_NODATA); - const int mip_width = _sg_miplevel_dim(width, mip_index); - const int mip_height = _sg_miplevel_dim(height, mip_index); - const int bytes_per_slice = _sg_surface_pitch(fmt, mip_width, mip_height, 1); - const int expected_size = bytes_per_slice * num_slices; - _SG_VALIDATE(expected_size == (int)data->mip_levels[mip_index].size, VALIDATE_IMAGEDATA_DATA_SIZE); - } - #endif -} - _SOKOL_PRIVATE bool _sg_validate_image_desc(const sg_image_desc* desc) { #if !defined(SOKOL_DEBUG) _SOKOL_UNUSED(desc); @@ -24871,48 +25259,21 @@ _SOKOL_PRIVATE bool _sg_validate_image_desc(const sg_image_desc* desc) { return true; } SOKOL_ASSERT(desc); + bool requires_initial_data = true; const sg_image_usage* usg = &desc->usage; + const sg_pixel_format fmt = desc->pixel_format; const bool any_attachment = usg->color_attachment || usg->resolve_attachment || usg->depth_stencil_attachment; + const bool injected = (0 != desc->gl_texture) || (0 != desc->mtl_texture) || (0 != desc->d3d11_texture) || (0 != desc->wgpu_texture); _sg_validate_begin(); _SG_VALIDATE(desc->_start_canary == 0, VALIDATE_IMAGEDESC_CANARY); _SG_VALIDATE(desc->_end_canary == 0, VALIDATE_IMAGEDESC_CANARY); - _SG_VALIDATE(_sg_one(usg->immutable, usg->write_transient, usg->copy_dst), VALIDATE_IMAGEDESC_IMMUTABLE_VS_WRITABLE); - _SG_VALIDATE(!(usg->color_attachment && usg->depth_stencil_attachment), VALIDATE_IMAGEDESC_ATTACHMENT_COLOR_DEPTH_STENCIL); - switch (desc->type) { - case SG_IMAGETYPE_2D: - _SG_VALIDATE(desc->num_slices == 1, VALIDATE_IMAGEDESC_IMAGETYPE_2D_NUMSLICES); - break; - case SG_IMAGETYPE_CUBE: - _SG_VALIDATE(desc->num_slices == 6, VALIDATE_IMAGEDESC_IMAGETYPE_CUBE_NUMSLICES); - break; - case SG_IMAGETYPE_ARRAY: - _SG_VALIDATE((desc->num_slices >= 1) && (desc->num_slices <= _sg.limits.max_image_array_layers), VALIDATE_IMAGEDESC_IMAGETYPE_ARRAY_NUMSLICES); - break; - case SG_IMAGETYPE_3D: - _SG_VALIDATE((desc->num_slices >= 1) && (desc->num_slices <= _sg.limits.max_image_size_3d), VALIDATE_IMAGEDESC_IMAGETYPE_3D_NUMSLICES); - break; - default: - SOKOL_UNREACHABLE; - break; - } - _SG_VALIDATE(desc->width > 0, VALIDATE_IMAGEDESC_WIDTH); - _SG_VALIDATE(desc->height > 0, VALIDATE_IMAGEDESC_HEIGHT); - const sg_pixel_format fmt = desc->pixel_format; - const bool injected = (0 != desc->gl_texture) || - (0 != desc->mtl_texture) || - (0 != desc->d3d11_texture) || - (0 != desc->wgpu_texture); - if (_sg_is_depth_or_depth_stencil_format(fmt)) { - _SG_VALIDATE(desc->type != SG_IMAGETYPE_3D, VALIDATE_IMAGEDESC_DEPTH_3D_IMAGE); - } if (any_attachment) { - SOKOL_ASSERT(((int)fmt >= 0) && ((int)fmt < _SG_PIXELFORMAT_NUM)); - _SG_VALIDATE(usg->immutable, VALIDATE_IMAGEDESC_ATTACHMENT_EXPECT_IMMUTABLE); - _SG_VALIDATE(desc->data.mip_levels[0].ptr==0, VALIDATE_IMAGEDESC_ATTACHMENT_EXPECT_NO_DATA); + requires_initial_data = false; + _SG_VALIDATE(!usg->write_unsealed, VALIDATE_IMAGEDESC_ANY_ATTACHMENT_VS_WRITEUNSEALED); + _SG_VALIDATE(!usg->write_transient, VALIDATE_IMAGEDESC_ANY_ATTACHMENT_VS_WRITETRANSIENT); + _SG_VALIDATE(!usg->copy_dst, VALIDATE_IMAGEDESC_ANY_ATTACHMENT_VS_COPYDST); + _SG_VALIDATE(0 == desc->data.ptr, VALIDATE_IMAGEDESC_ANY_ATTACHMENT_VS_INITIALDATA); _SG_VALIDATE(_sg.formats[fmt].render, VALIDATE_IMAGEDESC_ATTACHMENT_PIXELFORMAT); - if (usg->resolve_attachment) { - _SG_VALIDATE(desc->sample_count == 1, VALIDATE_IMAGEDESC_ATTACHMENT_RESOLVE_EXPECT_NO_MSAA); - } if (desc->sample_count > 1) { _SG_VALIDATE(_sg.formats[fmt].msaa, VALIDATE_IMAGEDESC_ATTACHMENT_NO_MSAA_SUPPORT); _SG_VALIDATE(desc->num_mipmaps == 1, VALIDATE_IMAGEDESC_ATTACHMENT_MSAA_NUM_MIPMAPS); @@ -24920,50 +25281,87 @@ _SOKOL_PRIVATE bool _sg_validate_image_desc(const sg_image_desc* desc) { _SG_VALIDATE(desc->type != SG_IMAGETYPE_3D, VALIDATE_IMAGEDESC_ATTACHMENT_MSAA_3D_IMAGE); _SG_VALIDATE(desc->type != SG_IMAGETYPE_CUBE, VALIDATE_IMAGEDESC_ATTACHMENT_MSAA_CUBE_IMAGE); } + } else { + _SG_VALIDATE(desc->sample_count == 1, VALIDATE_IMAGEDESC_MSAA_BUT_NO_ATTACHMENT); + } + if (usg->color_attachment) { + _SG_VALIDATE(!usg->depth_stencil_attachment, VALIDATE_IMAGEDESC_COLOR_ATTACHMENT_VS_DEPTHSTENCIL_ATTACHMENT); + _SG_VALIDATE(_sg_is_valid_attachment_color_format(fmt), VALIDATE_IMAGEDESC_COLOR_ATTACHMENT_PIXELFORMAT); + } + if (usg->resolve_attachment) { + _SG_VALIDATE(!usg->depth_stencil_attachment, VALIDATE_IMAGEDESC_RESOLVE_ATTACHMENT_VS_DEPTHSTENCIL_ATTACHMENT); + _SG_VALIDATE(desc->sample_count == 1, VALIDATE_IMAGEDESC_RESOLVE_ATTACHMENT_EXPECT_NO_MSAA); + } + if (usg->depth_stencil_attachment) { + _SG_VALIDATE(!usg->storage_image, VALIDATE_IMAGEDESC_DEPTHSTENCIL_ATTACHMENT_VS_STORAGEIMAGE); + _SG_VALIDATE(_sg_is_valid_attachment_depth_format(fmt), VALIDATE_IMAGEDESC_DEPTHSTENCIL_ATTACHMENT_PIXELFORMAT); } if (usg->storage_image) { - _SG_VALIDATE(usg->immutable, VALIDATE_IMAGEDESC_STORAGEIMAGE_EXPECT_IMMUTABLE); - _SG_VALIDATE(desc->data.mip_levels[0].ptr==0, VALIDATE_IMAGEDESC_STORAGEIMAGE_EXPECT_NO_DATA); + requires_initial_data = false; + _SG_VALIDATE(!usg->write_transient, VALIDATE_IMAGEDESC_STORAGEIMAGE_VS_WRITETRANSIENT); _SG_VALIDATE(_sg_is_valid_storage_image_format(fmt), VALIDATE_IMAGEDESC_STORAGEIMAGE_PIXELFORMAT); // D3D11 doesn't allow multisampled UAVs (see: https://github.com/gpuweb/gpuweb/issues/513) _SG_VALIDATE(desc->sample_count == 1, VALIDATE_IMAGEDESC_STORAGEIMAGE_EXPECT_NO_MSAA); + _SG_VALIDATE(0 == desc->data.ptr, VALIDATE_IMAGEDESC_STORAGEIMAGE_VS_INITIALDATA); } if (usg->write_unsealed) { - _SG_VALIDATE(usg->immutable, VALIDATE_IMAGEDESC_WRITEUNSEALED_VS_IMMUTABLE); - _SG_VALIDATE(!any_attachment, VALIDATE_IMAGEDESC_WRITEUNSEALED_VS_ATTACHMENT); + requires_initial_data = false; + _SG_VALIDATE(!usg->write_transient, VALIDATE_IMAGEDESC_WRITEUNSEALED_VS_WRITETRANSIENT); + _SG_VALIDATE(!usg->copy_dst, VALIDATE_IMAGEDESC_WRITEUNSEALED_VS_COPYDST); + _SG_VALIDATE(0 == desc->data.ptr, VALIDATE_IMAGEDESC_WRITEUNSEALED_VS_INITIALDATA); } if (usg->write_transient) { - _SG_VALIDATE(!any_attachment, VALIDATE_IMAGEDESC_WRITETRANSIENT_VS_ATTACHMENT); + requires_initial_data = false; + _SG_VALIDATE(!usg->copy_dst, VALIDATE_IMAGEDESC_WRITETRANSIENT_VS_COPYDST); _SG_VALIDATE(!injected, VALIDATE_IMAGEDESC_WRITETRANSIENT_VS_INJECTED); + _SG_VALIDATE(0 == desc->data.ptr, VALIDATE_IMAGEDESC_WRITETRANSIENT_VS_INITIALDATA); } if (usg->copy_dst) { - _SG_VALIDATE(!any_attachment, VALIDATE_IMAGEDESC_COPYDST_VS_ATTACHMENT); + requires_initial_data = false; + _SG_VALIDATE(0 == desc->data.ptr, VALIDATE_IMAGEDESC_COPYDST_VS_INITIALDATA); } - if (!any_attachment && !usg->storage_image) { - _SG_VALIDATE(desc->sample_count == 1, VALIDATE_IMAGEDESC_MSAA_BUT_NO_ATTACHMENT); - const bool valid_nonrt_fmt = !_sg_is_valid_attachment_depth_format(fmt); - _SG_VALIDATE(valid_nonrt_fmt, VALIDATE_IMAGEDESC_NONRT_PIXELFORMAT); - if (!injected && !usg->write_unsealed && usg->immutable) { - // image desc must have valid data - _sg_validate_image_data(&desc->data, - desc->pixel_format, - desc->width, - desc->height, - desc->num_mipmaps, - desc->num_slices); - } else { - // image desc must not have data - for (int mip_index = 0; mip_index < SG_MAX_MIPMAPS; mip_index++) { - const bool no_data = 0 == desc->data.mip_levels[mip_index].ptr; - const bool no_size = 0 == desc->data.mip_levels[mip_index].size; - if (injected) { - _SG_VALIDATE(no_data && no_size, VALIDATE_IMAGEDESC_INJECTED_NO_DATA); - } - if (!usg->immutable || usg->write_unsealed) { - _SG_VALIDATE(no_data && no_size, VALIDATE_IMAGEDESC_WRITABLE_NO_DATA); - } - } + if (_sg_is_depth_or_depth_stencil_format(fmt)) { + _SG_VALIDATE(usg->depth_stencil_attachment || usg->copy_dst, VALIDATE_IMAGEDESC_DEPTHFORMAT_ATTACHMENT_OR_COPYDST); + _SG_VALIDATE(desc->type != SG_IMAGETYPE_3D, VALIDATE_IMAGEDESC_DEPTHFORMAT_3D_IMAGE); + } + switch (desc->type) { + case SG_IMAGETYPE_2D: + _SG_VALIDATE(desc->num_slices == 1, VALIDATE_IMAGEDESC_IMAGETYPE_2D_NUMSLICES); + break; + case SG_IMAGETYPE_CUBE: + _SG_VALIDATE(desc->num_slices == 6, VALIDATE_IMAGEDESC_IMAGETYPE_CUBE_NUMSLICES); + break; + case SG_IMAGETYPE_ARRAY: + _SG_VALIDATE((desc->num_slices >= 1) && (desc->num_slices <= _sg.limits.max_image_array_layers), VALIDATE_IMAGEDESC_IMAGETYPE_ARRAY_NUMSLICES); + break; + case SG_IMAGETYPE_3D: + _SG_VALIDATE((desc->num_slices >= 1) && (desc->num_slices <= _sg.limits.max_image_size_3d), VALIDATE_IMAGEDESC_IMAGETYPE_3D_NUMSLICES); + break; + default: + SOKOL_UNREACHABLE; + break; + } + _SG_VALIDATE(desc->width > 0, VALIDATE_IMAGEDESC_WIDTH); + _SG_VALIDATE(desc->height > 0, VALIDATE_IMAGEDESC_HEIGHT); + if (desc->data.ptr) { + size_t expected_size = 0; + for (int mip_index = 0; mip_index < desc->num_mipmaps; mip_index++) { + const int mip_width = _sg_miplevel_dim(desc->width, mip_index); + const int mip_height = _sg_miplevel_dim(desc->height, mip_index); + const int mip_slices = (SG_IMAGETYPE_3D == desc->type) ? _sg_miplevel_dim(desc->num_slices, mip_index) : desc->num_slices; + const int bytes_per_slice = _sg_surface_pitch(fmt, mip_width, mip_height, 1); + expected_size += (size_t)bytes_per_slice * (size_t)mip_slices; } + _SG_VALIDATE(expected_size == desc->data.size, VALIDATE_IMAGEDESC_DATA_SIZE); + } else { + _SG_VALIDATE(0 == desc->data.size, VALIDATE_IMAGEDESC_NO_DATA_BUT_SIZE_SET); + } + if (injected) { + requires_initial_data = false; + _SG_VALIDATE(0 == desc->data.size, VALIDATE_IMAGEDESC_INJECTED_EXPECT_NO_DATA); + } + if (requires_initial_data) { + _SG_VALIDATE(desc->data.ptr, VALIDATE_IMAGEDESC_INITIAL_DATA_REQUIRED); } return _sg_validate_end(); #endif @@ -26403,9 +26801,10 @@ _SOKOL_PRIVATE bool _sg_validate_write_image_transient(const _sg_image_t* img, c SOKOL_ASSERT(img && desc); SOKOL_ASSERT(img->slot.state == SG_RESOURCESTATE_VALID); _sg_validate_begin(); - _SG_VALIDATE(!img->cmn.usage.immutable && img->cmn.usage.write_transient, VALIDATE_WRITEIMAGETRANSIENT_USAGE); - // write-transient is only allowed before resource is bound in current frame + _SG_VALIDATE(img->cmn.usage.write_transient, VALIDATE_WRITEIMAGETRANSIENT_USAGE); + // write-transient is only allowed before resource is 'used' in current frame _SG_VALIDATE(img->cmn.validate.bind_frame_index != _sg.frame_index, VALIDATE_WRITEIMAGETRANSIENT_WRITE_BEFORE_BIND); + _SG_VALIDATE(img->cmn.validate.copy_src_frame_index != _sg.frame_index, VALIDATE_WRITEIMAGETRANSIENT_WRITE_BEFORE_COPY); _sg_validate_write_image_common(img, desc); return _sg_validate_end(); #endif @@ -26423,7 +26822,7 @@ _SOKOL_PRIVATE bool _sg_validate_write_image_unsealed(const _sg_image_t* img, co SOKOL_ASSERT(img && desc); SOKOL_ASSERT(img->slot.state == SG_RESOURCESTATE_UNSEALED); _sg_validate_begin(); - _SG_VALIDATE(img->cmn.usage.immutable && img->cmn.usage.write_unsealed, VALIDATE_WRITEIMAGEUNSEALED_USAGE); + _SG_VALIDATE(img->cmn.usage.write_unsealed, VALIDATE_WRITEIMAGEUNSEALED_USAGE); _sg_validate_write_image_common(img, desc); return _sg_validate_end(); #endif @@ -26514,6 +26913,8 @@ _SOKOL_PRIVATE bool _sg_validate_copy_buffer_to_image(_sg_buffer_t* src_buf, con const int bpr = desc->src.bytes_per_row; const int bps = desc->src.bytes_per_slice; const bool is_staging_buffer = src_buf->cmn.usage.staging_buffer; + // cannot copy into depth-(stencil-)images + _SG_VALIDATE(!_sg_is_depth_or_depth_stencil_format(fmt), VALIDATE_COPYBUFFERTOIMAGE_DEPTH_FORMAT_NOT_ALLOWED); // NOTE: the <= 0 check prevents a misleading validation message _SG_VALIDATE((bpr <= 0) || (bpr >= row_bytes), VALIDATE_COPYBUFFERTOIMAGE_BYTESPERROW_TOO_SMALL); _SG_VALIDATE((bps <= 0) || (bps >= (num_rows * bpr)), VALIDATE_COPYBUFFERTOIMAGE_BYTESPERSLICE_TOO_SMALL); @@ -26563,6 +26964,77 @@ _SOKOL_PRIVATE bool _sg_validate_copy_buffer_to_image(_sg_buffer_t* src_buf, con #endif } +_SOKOL_PRIVATE bool _sg_validate_copy_image_to_image(_sg_image_t* src_img, const _sg_image_t* dst_img, const sg_copy_image_to_image_desc* desc) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(src_img && dst_img && desc); + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + SOKOL_ASSERT(src_img && dst_img && desc); + SOKOL_ASSERT(src_img->slot.state == SG_RESOURCESTATE_VALID); + SOKOL_ASSERT(dst_img->slot.state == SG_RESOURCESTATE_VALID); + src_img->cmn.validate.copy_src_frame_index = _sg.frame_index; + _sg_validate_begin(); + const sg_pixel_format src_fmt = src_img->cmn.pixel_format; + const sg_pixel_format dst_fmt = dst_img->cmn.pixel_format; + const int src_mip_width = _sg_miplevel_dim(src_img->cmn.width, desc->src.mip_level); + const int src_mip_height = _sg_miplevel_dim(src_img->cmn.height, desc->src.mip_level); + const int src_mip_depth_or_slices = (SG_IMAGETYPE_3D == src_img->cmn.type) ? _sg_miplevel_dim(src_img->cmn.num_slices, desc->src.mip_level) : src_img->cmn.num_slices; + const int dst_mip_width = _sg_miplevel_dim(dst_img->cmn.width, desc->dst.mip_level); + const int dst_mip_height = _sg_miplevel_dim(dst_img->cmn.height, desc->dst.mip_level); + const int dst_mip_depth_or_slices = (SG_IMAGETYPE_3D == dst_img->cmn.type) ? _sg_miplevel_dim(dst_img->cmn.num_slices, desc->dst.mip_level) : dst_img->cmn.num_slices; + const int block_dim = _sg_block_dim(src_fmt); + _SG_VALIDATE(desc->src.image.id != desc->dst.image.id, VALIDATE_COPYIMAGETOIMAGE_SRC_VS_DST_IMAGE); + _SG_VALIDATE(!_sg.cur_pass.in_pass, VALIDATE_COPYIMAGETOIMAGE_INSIDE_PASS); + _SG_VALIDATE(src_img->cmn.usage.copy_src, VALIDATE_COPYIMAGETOIMAGE_COPY_SRC); + _SG_VALIDATE(dst_img->cmn.usage.copy_dst, VALIDATE_COPYIMAGETOIMAGE_COPY_DST); + _SG_VALIDATE((src_img->cmn.type == SG_IMAGETYPE_3D) == (dst_img->cmn.type == SG_IMAGETYPE_3D), VALIDATE_COPYIMAGETOIMAGE_3D_VS_OTHER); + _SG_VALIDATE(src_fmt == dst_fmt, VALIDATE_COPYIMAGETOIMAGE_SRC_VS_DST_FORMAT); + _SG_VALIDATE(src_img->cmn.sample_count == dst_img->cmn.sample_count, VALIDATE_COPYIMAGETOIMAGE_SRC_VS_DST_SAMPLE_COUNT); + if (!_sg.features.copy_image_to_image_relaxed) { + // additional restriction: pixel format must be renderable + _SG_VALIDATE(_sg.formats[src_fmt].render, VALIDATE_COPYIMAGETOIMAGE_RENDERABLE); + // additional restriction: images cannot be multi-sampled + _SG_VALIDATE(src_img->cmn.sample_count == 1, VALIDATE_COPYIMAGETOIMAGE_MSAA); + } + if ((src_img->cmn.sample_count > 1) || _sg_is_depth_or_depth_stencil_format(src_fmt)) { + _SG_VALIDATE((src_mip_width == dst_mip_width) && (src_mip_height == dst_mip_height), VALIDATE_COPYIMAGETOIMAGE_SRC_VS_DST_SIZE); + _SG_VALIDATE((src_mip_width == desc->size.width) && (src_mip_height == desc->size.height), VALIDATE_COPYIMAGETOIMAGE_WHOLE_MIPLEVEL); + } + _SG_VALIDATE((desc->src.mip_level >= 0) && (desc->src.mip_level < src_img->cmn.num_mipmaps), VALIDATE_COPYIMAGETOIMAGE_SRC_MIPLEVEL); + _SG_VALIDATE((desc->dst.mip_level >= 0) && (desc->dst.mip_level < dst_img->cmn.num_mipmaps), VALIDATE_COPYIMAGETOIMAGE_DST_MIPLEVEL); + _SG_VALIDATE((desc->size.width > 0) && (desc->size.width <= src_mip_width), VALIDATE_COPYIMAGETOIMAGE_SRC_WIDTH); + _SG_VALIDATE((desc->size.width > 0) && (desc->size.width <= dst_mip_width), VALIDATE_COPYIMAGETOIMAGE_DST_WIDTH); + _SG_VALIDATE((desc->size.height > 0) && (desc->size.height <= src_mip_height), VALIDATE_COPYIMAGETOIMAGE_SRC_HEIGHT); + _SG_VALIDATE((desc->size.height > 0) && (desc->size.height <= dst_mip_height), VALIDATE_COPYIMAGETOIMAGE_DST_HEIGHT); + _SG_VALIDATE((desc->size.width <= 0) || _sg_multiple(desc->size.width, block_dim) || ((desc->src.x + desc->size.width) == src_mip_width), VALIDATE_COPYIMAGETOIMAGE_SRC_WIDTH_MULTIPLE); + _SG_VALIDATE((desc->size.height <= 0) || _sg_multiple(desc->size.height, block_dim) || ((desc->src.y + desc->size.height) == src_mip_height), VALIDATE_COPYIMAGETOIMAGE_SRC_HEIGHT_MULTIPLE); + _SG_VALIDATE((desc->size.width <= 0) || _sg_multiple(desc->size.width, block_dim) || ((desc->dst.x + desc->size.width) == dst_mip_width), VALIDATE_COPYIMAGETOIMAGE_DST_WIDTH_MULTIPLE); + _SG_VALIDATE((desc->size.height <= 0) || _sg_multiple(desc->size.height, block_dim) || ((desc->dst.y + desc->size.height) == dst_mip_height), VALIDATE_COPYIMAGETOIMAGE_DST_HEIGHT_MULTIPLE); + _SG_VALIDATE((desc->size.num_slices > 0) && (desc->size.num_slices <= src_mip_depth_or_slices), VALIDATE_COPYIMAGETOIMAGE_SRC_NUMSLICES); + _SG_VALIDATE((desc->size.num_slices > 0) && (desc->size.num_slices <= dst_mip_depth_or_slices), VALIDATE_COPYIMAGETOIMAGE_DST_NUMSLICES); + _SG_VALIDATE((desc->src.x >= 0) && (desc->src.x < src_mip_width), VALIDATE_COPYIMAGETOIMAGE_SRC_X_RANGE); + _SG_VALIDATE((desc->dst.x >= 0) && (desc->dst.x < dst_mip_width), VALIDATE_COPYIMAGETOIMAGE_DST_X_RANGE); + _SG_VALIDATE((desc->src.y >= 0) && (desc->src.y < src_mip_height), VALIDATE_COPYIMAGETOIMAGE_SRC_Y_RANGE); + _SG_VALIDATE((desc->dst.y >= 0) && (desc->dst.y < dst_mip_height), VALIDATE_COPYIMAGETOIMAGE_DST_Y_RANGE); + _SG_VALIDATE((desc->src.x < 0) || _sg_multiple(desc->src.x, block_dim), VALIDATE_COPYIMAGETOIMAGE_SRC_X_ALIGNMENT); + _SG_VALIDATE((desc->dst.x < 0) || _sg_multiple(desc->dst.x, block_dim), VALIDATE_COPYIMAGETOIMAGE_DST_X_ALIGNMENT); + _SG_VALIDATE((desc->src.y < 0) || _sg_multiple(desc->src.y, block_dim), VALIDATE_COPYIMAGETOIMAGE_SRC_Y_ALIGNMENT); + _SG_VALIDATE((desc->dst.y < 0) || _sg_multiple(desc->dst.y, block_dim), VALIDATE_COPYIMAGETOIMAGE_DST_Y_ALIGNMENT); + _SG_VALIDATE((desc->src.slice >= 0) && (desc->src.slice < src_mip_depth_or_slices), VALIDATE_COPYIMAGETOIMAGE_SRC_SLICE_RANGE); + _SG_VALIDATE((desc->dst.slice >= 0) && (desc->dst.slice < dst_mip_depth_or_slices), VALIDATE_COPYIMAGETOIMAGE_DST_SLICE_RANGE); + _SG_VALIDATE((desc->src.x + desc->size.width) <= src_mip_width, VALIDATE_COPYIMAGETOIMAGE_SRC_WIDTH_OVERFLOW); + _SG_VALIDATE((desc->dst.x + desc->size.width) <= dst_mip_width, VALIDATE_COPYIMAGETOIMAGE_DST_WIDTH_OVERFLOW); + _SG_VALIDATE((desc->src.y + desc->size.height) <= src_mip_height, VALIDATE_COPYIMAGETOIMAGE_SRC_HEIGHT_OVERFLOW); + _SG_VALIDATE((desc->dst.y + desc->size.height) <= dst_mip_height, VALIDATE_COPYIMAGETOIMAGE_DST_HEIGHT_OVERFLOW); + _SG_VALIDATE((desc->src.slice + desc->size.num_slices) <= src_mip_depth_or_slices, VALIDATE_COPYIMAGETOIMAGE_SRC_NUMSLICES_OVERFLOW); + _SG_VALIDATE((desc->dst.slice + desc->size.num_slices) <= dst_mip_depth_or_slices, VALIDATE_COPYIMAGETOIMAGE_DST_NUMSLICES_OVERFLOW); + return _sg_validate_end(); + #endif +} + _SOKOL_PRIVATE bool _sg_validate_shader_binding_limits(const sg_shader_desc* desc) { SOKOL_ASSERT(desc); @@ -26716,9 +27188,7 @@ _SOKOL_PRIVATE sg_buffer_desc _sg_buffer_desc_defaults(const sg_buffer_desc* des _SOKOL_PRIVATE sg_image_usage _sg_image_usage_defaults(const sg_image_usage *usg) { sg_image_usage def = *usg; - if (!(def.immutable || def.write_transient || def.copy_dst)) { - def.immutable = true; - } + // NOTE: currently it's expected that no defaults are patched return def; } @@ -27351,6 +27821,17 @@ _SOKOL_PRIVATE sg_copy_buffer_to_image_desc _sg_copy_buffer_to_image_desc_defaul return res; } +_SOKOL_PRIVATE sg_copy_image_to_image_desc _sg_copy_image_to_image_desc_defaults(const _sg_image_t* src_img, const sg_copy_image_to_image_desc* desc) { + sg_copy_image_to_image_desc res = *desc; + const int mip_width = _sg_miplevel_dim(src_img->cmn.width, desc->src.mip_level); + const int mip_height = _sg_miplevel_dim(src_img->cmn.height, desc->src.mip_level); + const int mip_depth_or_slices = (SG_IMAGETYPE_3D == src_img->cmn.type) ? _sg_miplevel_dim(src_img->cmn.num_slices, desc->src.mip_level) : src_img->cmn.num_slices; + res.size.width = _sg_def(res.size.width, mip_width - desc->src.x); + res.size.height = _sg_def(res.size.height, mip_height - desc->src.y); + res.size.num_slices = _sg_def(res.size.num_slices, mip_depth_or_slices - desc->src.slice); + return res; +} + _SOKOL_PRIVATE void _sg_discard_all_resources(void) { /* this is a bit dumb since it loops over all pool slots to find the occupied slots, on the other hand it is only ever @@ -28641,6 +29122,43 @@ SOKOL_API_IMPL void sg_copy_buffer_to_image(const sg_copy_buffer_to_image_desc* _SG_TRACE_ARGS(copy_buffer_to_image, desc); } +SOKOL_API_IMPL void sg_copy_image_to_image(const sg_copy_image_to_image_desc* desc) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(desc); + _sg_stats_inc(num_copy_image_to_image); + _sg_image_t* src_img = _sg_lookup_image(desc->src.image.id); + _sg_image_t* dst_img = _sg_lookup_image(desc->dst.image.id); + if (!src_img) { + _SG_ERROR(COPY_IMAGE_TO_IMAGE_SRC_ALIVE); + return; + } + if (!dst_img) { + _SG_ERROR(COPY_IMAGE_TO_IMAGE_DST_ALIVE); + return; + } + if (src_img->slot.state != SG_RESOURCESTATE_VALID) { + _SG_ERROR(COPY_IMAGE_TO_IMAGE_SRC_VALID); + return; + } + if (dst_img->slot.state != SG_RESOURCESTATE_VALID) { + _SG_ERROR(COPY_IMAGE_TO_IMAGE_DST_VALID); + return; + } + sg_copy_image_to_image_desc desc_def = _sg_copy_image_to_image_desc_defaults(src_img, desc); + if (_sg_validate_copy_image_to_image(src_img, dst_img, &desc_def)) { + const sg_pixel_format fmt = src_img->cmn.pixel_format; + const size_t stats_copy_size = _sg_image_copy_size(fmt, + _sg_row_pitch(fmt, desc_def.size.width, 1), + _sg_surface_pitch(fmt, desc_def.size.width, desc_def.size.height, 1), + desc_def.size.width, + desc_def.size.height, + desc_def.size.num_slices); + _sg_stats_add(size_copy_image_to_image, (uint32_t)stats_copy_size); + _sg_copy_image_to_image(src_img, dst_img, &desc_def); + } + _SG_TRACE_ARGS(copy_image_to_image, desc); +} + SOKOL_API_IMPL void sg_push_debug_group(const char* name) { SOKOL_ASSERT(_sg.valid); SOKOL_ASSERT(name); @@ -29183,10 +29701,12 @@ SOKOL_API_IMPL sg_d3d11_view_info sg_d3d11_query_view_info(sg_view view_id) { _SG_STRUCT(sg_d3d11_view_info, res); #if defined(SOKOL_D3D11) const _sg_view_t* view = _sg_lookup_view(view_id.id); - res.srv = (const void*) view->d3d11.srv; - res.uav = (const void*) view->d3d11.uav; - res.rtv = (const void*) view->d3d11.rtv; - res.dsv = (const void*) view->d3d11.dsv; + if (view) { + res.srv = (const void*) view->d3d11.srv; + res.uav = (const void*) view->d3d11.uav; + res.rtv = (const void*) view->d3d11.rtv; + res.dsv = (const void*) view->d3d11.dsv; + } #else _SOKOL_UNUSED(view_id); #endif diff --git a/src/sokol/c/sokol_gfx_imgui.h b/src/sokol/c/sokol_gfx_imgui.h index 4e7b9e5..86e6762 100644 --- a/src/sokol/c/sokol_gfx_imgui.h +++ b/src/sokol/c/sokol_gfx_imgui.h @@ -3741,7 +3741,6 @@ _SOKOL_PRIVATE void _sgimgui_draw_image_panel(_sgimgui_t* ctx, sg_image img) { _sgimgui_igtext(" color_attachment: %s", _sgimgui_bool_string(desc->usage.color_attachment)); _sgimgui_igtext(" resolve_attachment: %s", _sgimgui_bool_string(desc->usage.resolve_attachment)); _sgimgui_igtext(" depth_stencil_attachment: %s", _sgimgui_bool_string(desc->usage.depth_stencil_attachment)); - _sgimgui_igtext(" immutable: %s", _sgimgui_bool_string(desc->usage.immutable)); _sgimgui_igtext(" write_unsealed: %s", _sgimgui_bool_string(desc->usage.write_unsealed)); _sgimgui_igtext(" write_transient: %s", _sgimgui_bool_string(desc->usage.write_transient)); _sgimgui_igtext(" copy_src: %s", _sgimgui_bool_string(desc->usage.copy_src)); @@ -3752,11 +3751,9 @@ _SOKOL_PRIVATE void _sgimgui_draw_image_panel(_sgimgui_t* ctx, sg_image img) { _sgimgui_igtext("Num Mipmaps: %d", desc->num_mipmaps); _sgimgui_igtext("Pixel Format: %s", _sgimgui_pixelformat_string(desc->pixel_format)); _sgimgui_igtext("Sample Count: %d", desc->sample_count); - if (!desc->usage.immutable) { - _sgimgui_igseparator(); - _sgimgui_igtext("Num Slots: %d", info.num_slots); - _sgimgui_igtext("Active Slot: %d", info.active_slot); - } + _sgimgui_igseparator(); + _sgimgui_igtext("Num Slots: %d", info.num_slots); + _sgimgui_igtext("Active Slot: %d", info.active_slot); } else { _sgimgui_igtext("Image 0x%08X not valid.", img.id); } @@ -4812,9 +4809,11 @@ _SOKOL_PRIVATE void _sgimgui_draw_frame_stats_panel(_sgimgui_t* ctx) { _sgimgui_frame_stats(prev_frame.num_seal_image); _sgimgui_frame_stats(prev_frame.num_copy_buffer_to_buffer); _sgimgui_frame_stats(prev_frame.num_copy_buffer_to_image); + _sgimgui_frame_stats(prev_frame.num_copy_image_to_image); _sgimgui_frame_stats(prev_frame.size_apply_uniforms); _sgimgui_frame_stats(prev_frame.size_copy_buffer_to_buffer); _sgimgui_frame_stats(prev_frame.size_copy_buffer_to_image); + _sgimgui_frame_stats(prev_frame.size_copy_image_to_image); _sgimgui_frame_stats(prev_frame.buffers.allocated); _sgimgui_frame_stats(prev_frame.buffers.deallocated); _sgimgui_frame_stats(prev_frame.buffers.inited); @@ -4954,6 +4953,7 @@ _SOKOL_PRIVATE void _sgimgui_draw_frame_stats_panel(_sgimgui_t* ctx) { _sgimgui_frame_stats(prev_frame.vk.num_delete_queue_collected); _sgimgui_frame_stats(prev_frame.vk.num_cmd_copy_buffer); _sgimgui_frame_stats(prev_frame.vk.num_cmd_copy_buffer_to_image); + _sgimgui_frame_stats(prev_frame.vk.num_cmd_copy_image); _sgimgui_frame_stats(prev_frame.vk.num_cmd_set_descriptor_buffer_offsets); _sgimgui_frame_stats(prev_frame.vk.size_descriptor_buffer_writes); break; diff --git a/src/sokol/c/sokol_gl.h b/src/sokol/c/sokol_gl.h index 757d4fe..d0454fa 100644 --- a/src/sokol/c/sokol_gl.h +++ b/src/sokol/c/sokol_gl.h @@ -3453,7 +3453,7 @@ static void _sgl_setup_common(void) { img_desc.height = 8; img_desc.num_mipmaps = 1; img_desc.pixel_format = SG_PIXELFORMAT_RGBA8; - img_desc.data.mip_levels[0] = SG_RANGE(pixels); + img_desc.data = SG_RANGE(pixels); img_desc.label = "sgl-default-texture"; _sgl.def_img = sg_make_image(&img_desc); SOKOL_ASSERT(SG_INVALID_ID != _sgl.def_img.id);