Blender V4.3
mtl_texture.mm
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2022-2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include "BKE_global.hh"
10
11#include "DNA_userdef_types.h"
12
13#include "GPU_batch.hh"
14#include "GPU_batch_presets.hh"
15#include "GPU_capabilities.hh"
16#include "GPU_framebuffer.hh"
17#include "GPU_immediate.hh"
18#include "GPU_platform.hh"
19#include "GPU_state.hh"
20
21#include "mtl_backend.hh"
22#include "mtl_common.hh"
23#include "mtl_context.hh"
24#include "mtl_debug.hh"
25#include "mtl_storage_buffer.hh"
26#include "mtl_texture.hh"
27#include "mtl_vertex_buffer.hh"
28
29#include "GHOST_C-api.h"
30
31namespace blender::gpu {
32
33/* -------------------------------------------------------------------- */
37void gpu::MTLTexture::mtl_texture_init()
38{
39 BLI_assert(MTLContext::get() != nullptr);
40
41 /* Status. */
42 is_baked_ = false;
43 is_dirty_ = false;
44 resource_mode_ = MTL_TEXTURE_MODE_DEFAULT;
45 mtl_max_mips_ = 1;
46
47 /* Metal properties. */
48 texture_ = nil;
49 mip_swizzle_view_ = nil;
50
51 /* Binding information. */
52 is_bound_ = false;
53
54 /* VBO. */
55 vert_buffer_ = nullptr;
56 vert_buffer_mtl_ = nil;
57
58 /* Default Swizzle. */
59 tex_swizzle_mask_[0] = 'r';
60 tex_swizzle_mask_[1] = 'g';
61 tex_swizzle_mask_[2] = 'b';
62 tex_swizzle_mask_[3] = 'a';
63 mtl_swizzle_mask_ = MTLTextureSwizzleChannelsMake(
64 MTLTextureSwizzleRed, MTLTextureSwizzleGreen, MTLTextureSwizzleBlue, MTLTextureSwizzleAlpha);
65}
66
67gpu::MTLTexture::MTLTexture(const char *name) : Texture(name)
68{
69 /* Common Initialization. */
70 mtl_texture_init();
71}
72
75 eGPUTextureType type,
76 id<MTLTexture> metal_texture)
77 : Texture(name)
78{
79 /* Common Initialization. */
80 mtl_texture_init();
81
82 /* Prep texture from METAL handle. */
83 BLI_assert(metal_texture != nil);
85 type_ = type;
86 init_2D((int)metal_texture.width, (int)metal_texture.height, 0, 1, format);
87
88 /* Assign MTLTexture. */
89 texture_ = metal_texture;
90 [texture_ retain];
91 internal_gpu_image_usage_flags_ = gpu_usage_from_mtl(metal_texture.usage);
92 gpu_image_usage_flags_ = internal_gpu_image_usage_flags_;
93
94 /* Flag as Baked. */
95 is_baked_ = true;
96 is_dirty_ = false;
97 resource_mode_ = MTL_TEXTURE_MODE_EXTERNAL;
98}
99
101{
102 /* Unbind if bound. */
103 if (is_bound_) {
105 if (ctx != nullptr) {
106 ctx->state_manager->texture_unbind(this);
107 }
108 }
109
110 /* Free memory. */
111 this->reset();
112}
113
116/* -------------------------------------------------------------------- */
117void gpu::MTLTexture::bake_mip_swizzle_view()
118{
119 if (texture_view_dirty_flags_) {
120
121 /* Optimization: only generate texture view for mipmapped textures if base level > 0
122 * and max level does not match the existing number of mips.
123 * Only apply this if mipmap is the only change, and we have not previously generated
124 * a texture view. For textures which are created as views, this should also be skipped. */
125 if (resource_mode_ != MTL_TEXTURE_MODE_TEXTURE_VIEW &&
126 texture_view_dirty_flags_ == TEXTURE_VIEW_MIP_DIRTY && mip_swizzle_view_ == nil)
127 {
128
129 if (mip_texture_base_level_ == 0 && mip_texture_max_level_ == mtl_max_mips_) {
130 texture_view_dirty_flags_ = TEXTURE_VIEW_NOT_DIRTY;
131 return;
132 }
133 }
134
135 /* if a texture view was previously created we release it. */
136 if (mip_swizzle_view_ != nil) {
137 [mip_swizzle_view_ release];
138 mip_swizzle_view_ = nil;
139 }
140
141 /* Use source texture to determine range limits. If we are using a GPU texture view, the range
142 * check should only validate the range */
143 const gpu::Texture *tex_view_src = this;
144 if (resource_mode_ == MTL_TEXTURE_MODE_TEXTURE_VIEW) {
145 tex_view_src = unwrap(source_texture_);
146 }
147
148 /* Determine num slices */
149 int max_slices = 1;
150 int num_slices = 1;
151 switch (type_) {
153 max_slices = tex_view_src->height_get();
154 num_slices = h_;
155 break;
157 max_slices = tex_view_src->depth_get();
158 num_slices = d_;
159 break;
160 case GPU_TEXTURE_CUBE:
161 max_slices = 6;
162 num_slices = 6;
163 break;
165 /* d_ is equal to array levels * 6, including face count. */
166 max_slices = tex_view_src->depth_get();
167 num_slices = d_;
168 break;
169 default:
170 num_slices = 1;
171 break;
172 }
173
174 /* Determine texture view format. If texture view is used as a stencil view, we instead provide
175 * the equivalent format for performing stencil reads/samples. */
176 MTLPixelFormat texture_view_pixel_format = gpu_texture_format_to_metal(format_);
177 if (texture_view_stencil_) {
178 switch (texture_view_pixel_format) {
179 case MTLPixelFormatDepth24Unorm_Stencil8:
180 texture_view_pixel_format = MTLPixelFormatX24_Stencil8;
181 break;
182 case MTLPixelFormatDepth32Float_Stencil8:
183 texture_view_pixel_format = MTLPixelFormatX32_Stencil8;
184 break;
185 default:
186 BLI_assert_msg(false, "Texture format does not support stencil views.");
187 break;
188 }
189 }
190
191 /* NOTE: Texture type for cube maps can be overridden as a 2D array. This is done
192 * via modifying this textures type flags. */
193 MTLTextureType texture_view_texture_type = to_metal_type(type_);
194
195 /* Ensure we have texture view usage flagged.
196 * NOTE: This check exists in high level GPU API, however does not cover internal Metal backend
197 * uses of texture views such as when required to support SRGB enablement toggle during
198 * rendering. */
200 (texture_view_pixel_format == texture_.pixelFormat) ||
201 (internal_gpu_image_usage_flags_ & GPU_TEXTURE_USAGE_FORMAT_VIEW),
202 "Usage Flag GPU_TEXTURE_USAGE_FORMAT_VIEW must be specified if a texture view is "
203 "created with a different format to its source texture.");
204
205 int range_len = min_ii((mip_texture_max_level_ - mip_texture_base_level_) + 1,
206 (int)texture_.mipmapLevelCount - mip_texture_base_level_);
207 BLI_assert(range_len > 0);
208 BLI_assert(mip_texture_base_level_ < texture_.mipmapLevelCount);
209 BLI_assert(mip_texture_base_layer_ < max_slices);
210 UNUSED_VARS_NDEBUG(max_slices);
211 mip_swizzle_view_ = [texture_
212 newTextureViewWithPixelFormat:texture_view_pixel_format
213 textureType:texture_view_texture_type
214 levels:NSMakeRange(mip_texture_base_level_, range_len)
215 slices:NSMakeRange(mip_texture_base_layer_, num_slices)
216 swizzle:mtl_swizzle_mask_];
218 "Updating texture view - MIP TEXTURE BASE LEVEL: %d, MAX LEVEL: %d (Range len: %d)",
219 mip_texture_base_level_,
220 min_ii(mip_texture_max_level_, (int)texture_.mipmapLevelCount),
221 range_len);
222#ifndef NDEBUG
223 mip_swizzle_view_.label = [NSString
224 stringWithFormat:
225 @"MipSwizzleView_%s__format=%u_type=%u_baselevel=%u_numlevels=%u_swizzle='%c%c%c%c'",
226 [[texture_ label] UTF8String],
227 (uint)texture_view_pixel_format,
228 (uint)texture_view_texture_type,
229 (uint)mip_texture_base_level_,
230 (uint)range_len,
231 tex_swizzle_mask_[0],
232 tex_swizzle_mask_[1],
233 tex_swizzle_mask_[2],
234 tex_swizzle_mask_[3]];
235#else
236 mip_swizzle_view_.label = [texture_ label];
237#endif
238 texture_view_dirty_flags_ = TEXTURE_VIEW_NOT_DIRTY;
239 }
240}
241
245id<MTLTexture> gpu::MTLTexture::get_metal_handle()
246{
247
248 /* Verify VBO texture shares same buffer. */
249 if (resource_mode_ == MTL_TEXTURE_MODE_VBO) {
250 id<MTLBuffer> buf = vert_buffer_->get_metal_buffer();
251
252 /* Source vertex buffer has been re-generated, require re-initialization. */
253 if (buf != vert_buffer_mtl_) {
255 "MTLTexture '%p' using MTL_TEXTURE_MODE_VBO requires re-generation due to updated "
256 "Vertex-Buffer.",
257 this);
258 /* Clear state. */
259 this->reset();
260
261 /* Re-initialize. */
262 this->init_internal(vert_buffer_);
263
264 /* Update for assertion check below. */
265 buf = vert_buffer_->get_metal_buffer();
266 }
267
268 /* Ensure buffer is valid.
269 * Fetch-vert buffer handle directly in-case it changed above. */
270 BLI_assert(vert_buffer_mtl_ != nil);
271 BLI_assert(vert_buffer_->get_metal_buffer() == vert_buffer_mtl_);
272 }
273
274 /* ensure up to date and baked. */
275 this->ensure_baked();
276
277 if (is_baked_) {
278 /* For explicit texture views, ensure we always return the texture view. */
279 if (resource_mode_ == MTL_TEXTURE_MODE_TEXTURE_VIEW) {
280 BLI_assert_msg(mip_swizzle_view_, "Texture view should always have a valid handle.");
281 }
282
283 if (mip_swizzle_view_ != nil || texture_view_dirty_flags_) {
284 bake_mip_swizzle_view();
285
286 /* Optimization: If texture view does not change mip parameters, no texture view will be
287 * baked. This is because texture views remove the ability to perform lossless compression.
288 */
289 if (mip_swizzle_view_ != nil) {
290 return mip_swizzle_view_;
291 }
292 }
293 return texture_;
294 }
295 return nil;
296}
297
298id<MTLTexture> gpu::MTLTexture::get_metal_handle_base()
299{
300
301 /* ensure up to date and baked. */
302 this->ensure_baked();
303
304 /* For explicit texture views, always return the texture view. */
305 if (resource_mode_ == MTL_TEXTURE_MODE_TEXTURE_VIEW) {
306 BLI_assert_msg(mip_swizzle_view_, "Texture view should always have a valid handle.");
307 if (mip_swizzle_view_ != nil || texture_view_dirty_flags_) {
308 bake_mip_swizzle_view();
309 }
310 BLI_assert(mip_swizzle_view_ != nil);
311 return mip_swizzle_view_;
312 }
313
314 /* Return base handle. */
315 if (is_baked_) {
316 return texture_;
317 }
318 return nil;
319}
320
321void gpu::MTLTexture::blit(id<MTLBlitCommandEncoder> blit_encoder,
322 uint src_x_offset,
323 uint src_y_offset,
324 uint src_z_offset,
325 uint src_slice,
326 uint src_mip,
327 gpu::MTLTexture *dest,
328 uint dst_x_offset,
329 uint dst_y_offset,
330 uint dst_z_offset,
331 uint dst_slice,
332 uint dst_mip,
333 uint width,
334 uint height,
335 uint depth)
336{
337
338 BLI_assert(dest);
339 BLI_assert(width > 0 && height > 0 && depth > 0);
340 MTLSize src_size = MTLSizeMake(width, height, depth);
341 MTLOrigin src_origin = MTLOriginMake(src_x_offset, src_y_offset, src_z_offset);
342 MTLOrigin dst_origin = MTLOriginMake(dst_x_offset, dst_y_offset, dst_z_offset);
343
344 if (this->format_get() != dest->format_get()) {
346 "gpu::MTLTexture: Cannot copy between two textures of different types using a "
347 "blit encoder. TODO: Support this operation");
348 return;
349 }
350
351 /* TODO(Metal): Verify if we want to use the one with modified base-level/texture view
352 * or not. */
353 [blit_encoder copyFromTexture:this->get_metal_handle_base()
354 sourceSlice:src_slice
355 sourceLevel:src_mip
356 sourceOrigin:src_origin
357 sourceSize:src_size
358 toTexture:dest->get_metal_handle_base()
359 destinationSlice:dst_slice
360 destinationLevel:dst_mip
361 destinationOrigin:dst_origin];
362}
363
364void gpu::MTLTexture::blit(gpu::MTLTexture *dst,
365 uint src_x_offset,
366 uint src_y_offset,
367 uint dst_x_offset,
368 uint dst_y_offset,
369 uint src_mip,
370 uint dst_mip,
371 uint dst_slice,
372 int width,
373 int height)
374{
375 BLI_assert(this->type_get() == dst->type_get());
376
377 GPUShader *shader = fullscreen_blit_sh_get();
378 BLI_assert(shader != nullptr);
380
381 /* Fetch restore framebuffer and blit target framebuffer from destination texture. */
382 GPUFrameBuffer *restore_fb = GPU_framebuffer_active_get();
383 GPUFrameBuffer *blit_target_fb = dst->get_blit_framebuffer(dst_slice, dst_mip);
384 BLI_assert(blit_target_fb);
385 GPU_framebuffer_bind(blit_target_fb);
386
387 /* Execute graphics draw call to perform the blit. */
388 Batch *quad = GPU_batch_preset_quad();
389 GPU_batch_set_shader(quad, shader);
390
391 float w = dst->width_get();
392 float h = dst->height_get();
393
394 GPU_shader_uniform_2f(shader, "fullscreen", w, h);
395 GPU_shader_uniform_2f(shader, "src_offset", src_x_offset, src_y_offset);
396 GPU_shader_uniform_2f(shader, "dst_offset", dst_x_offset, dst_y_offset);
397 GPU_shader_uniform_2f(shader, "size", width, height);
398
399 GPU_shader_uniform_1i(shader, "mip", src_mip);
400 GPU_batch_texture_bind(quad, "imageTexture", wrap(this));
401
402 /* Caching previous pipeline state. */
403 bool depth_write_prev = GPU_depth_mask_get();
404 uint stencil_mask_prev = GPU_stencil_mask_get();
405 eGPUStencilTest stencil_test_prev = GPU_stencil_test_get();
406 eGPUFaceCullTest culling_test_prev = GPU_face_culling_get();
407 eGPUBlend blend_prev = GPU_blend_get();
408 eGPUDepthTest depth_test_prev = GPU_depth_test_get();
409 GPU_scissor_test(false);
410
411 /* Apply state for blit draw call. */
416 GPU_depth_mask(false);
419
421
422 /* restoring old pipeline state. */
423 GPU_depth_mask(depth_write_prev);
424 GPU_stencil_write_mask_set(stencil_mask_prev);
425 GPU_stencil_test(stencil_test_prev);
426 GPU_face_culling(culling_test_prev);
427 GPU_depth_mask(depth_write_prev);
428 GPU_blend(blend_prev);
429 GPU_depth_test(depth_test_prev);
430
431 if (restore_fb != nullptr) {
432 GPU_framebuffer_bind(restore_fb);
433 }
434 else {
436 }
437}
438
439GPUFrameBuffer *gpu::MTLTexture::get_blit_framebuffer(int dst_slice, uint dst_mip)
440{
441
442 /* Check if layer has changed. */
443 bool update_attachments = false;
444 if (!blit_fb_) {
445 blit_fb_ = GPU_framebuffer_create("gpu_blit");
446 update_attachments = true;
447 }
448
449 /* Check if current blit FB has the correct attachment properties. */
450 if (blit_fb_) {
451 if (blit_fb_slice_ != dst_slice || blit_fb_mip_ != dst_mip) {
452 update_attachments = true;
453 }
454 }
455
456 if (update_attachments) {
457 if (format_flag_ & GPU_FORMAT_DEPTH || format_flag_ & GPU_FORMAT_STENCIL) {
458 /* DEPTH TEX */
460 &blit_fb_,
461 {GPU_ATTACHMENT_TEXTURE_LAYER_MIP(wrap(static_cast<Texture *>(this)),
462 static_cast<int>(dst_slice),
463 static_cast<int>(dst_mip)),
465 }
466 else {
467 /* COLOR TEX */
469 &blit_fb_,
471 GPU_ATTACHMENT_TEXTURE_LAYER_MIP(wrap(static_cast<Texture *>(this)),
472 static_cast<int>(dst_slice),
473 static_cast<int>(dst_mip))});
474 }
475 blit_fb_slice_ = dst_slice;
476 blit_fb_mip_ = dst_mip;
477 }
478
479 BLI_assert(blit_fb_);
480 return blit_fb_;
481}
482
483MTLSamplerState gpu::MTLTexture::get_sampler_state()
484{
485 MTLSamplerState sampler_state;
486 sampler_state.state = this->sampler_state;
487 /* Add more parameters as needed */
488 return sampler_state;
489}
490
492 int mip, int offset[3], int extent[3], eGPUDataFormat type, const void *data)
493{
494 /* Fetch active context. */
496 BLI_assert(ctx);
497
498 /* Do not update texture view. */
499 BLI_assert(resource_mode_ != MTL_TEXTURE_MODE_TEXTURE_VIEW);
500
501 /* Ensure mipmaps. */
502 this->ensure_mipmaps(mip);
503
504 /* Ensure texture is baked. */
505 this->ensure_baked();
506
507 /* Safety checks. */
508#if TRUST_NO_ONE
509 BLI_assert(mip >= mip_min_ && mip <= mip_max_);
510 BLI_assert(mip < texture_.mipmapLevelCount);
511 BLI_assert(texture_.mipmapLevelCount >= mip_max_);
512#endif
513
514 /* DEPTH FLAG - Depth formats cannot use direct BLIT - pass off to their own routine which will
515 * do a depth-only render. */
516 bool is_depth_format = (format_flag_ & GPU_FORMAT_DEPTH);
517 if (is_depth_format) {
518 switch (type_) {
519
520 case GPU_TEXTURE_2D:
521 update_sub_depth_2d(mip, offset, extent, type, data);
522 return;
523 default:
525 "gpu::MTLTexture::update_sub not yet supported for other depth "
526 "configurations");
527 return;
528 }
529 }
530
531 const bool is_compressed = (format_flag_ & GPU_FORMAT_COMPRESSED);
532
533 @autoreleasepool {
534 /* Determine totalsize of INPUT Data. */
535 int num_channels = to_component_len(format_);
536 size_t input_bytes_per_pixel = to_bytesize(format_, type);
537 size_t totalsize = 0;
538
539 /* If unpack row length is used, size of input data uses the unpack row length, rather than the
540 * image length. */
541 size_t expected_update_w = ((ctx->pipeline_state.unpack_row_length == 0) ?
542 extent[0] :
544
545 /* Ensure calculated total size isn't larger than remaining image data size. */
546 if (is_compressed) {
547 /* Calculate size requirement for incoming compressed texture data. */
548 totalsize = ((expected_update_w + 3) / 4) * ((extent[1] + 3) / 4) * to_block_size(format_);
549 }
550 else {
551 switch (this->dimensions_count()) {
552 case 1:
553 totalsize = input_bytes_per_pixel * max_ulul(expected_update_w, 1);
554 break;
555 case 2:
556 totalsize = input_bytes_per_pixel * max_ulul(expected_update_w, 1) * (size_t)extent[1];
557 break;
558 case 3:
559 totalsize = input_bytes_per_pixel * max_ulul(expected_update_w, 1) * (size_t)extent[1] *
560 (size_t)extent[2];
561 break;
562 default:
563 BLI_assert(false);
564 break;
565 }
566 }
567
568 /* Early exit if update size is zero. update_sub sometimes has a zero-sized
569 * extent when called from texture painting. */
570 if (totalsize <= 0 || extent[0] <= 0) {
572 "MTLTexture::update_sub called with extent size of zero for one or more dimensions. "
573 "(%d, %d, %d) - DimCount: %u",
574 extent[0],
575 extent[1],
576 extent[2],
577 this->dimensions_count());
578 return;
579 }
580
581 /* When unpack row length is used, provided data does not necessarily contain padding for last
582 * row, so we only include up to the end of updated data. */
583 if (ctx->pipeline_state.unpack_row_length > 0) {
585 totalsize -= (ctx->pipeline_state.unpack_row_length - extent[0]) * input_bytes_per_pixel;
586 }
587
588 /* Check */
589 BLI_assert(totalsize > 0);
590
591 /* Determine expected destination data size. */
592 MTLPixelFormat destination_format = gpu_texture_format_to_metal(format_);
593 size_t expected_dst_bytes_per_pixel = get_mtl_format_bytesize(destination_format);
594 int destination_num_channels = get_mtl_format_num_components(destination_format);
595
596 /* Prepare specialization struct (For texture update routine). */
597 TextureUpdateRoutineSpecialisation compute_specialization_kernel = {
598 tex_data_format_to_msl_type_str(type), /* INPUT DATA FORMAT */
599 tex_data_format_to_msl_texture_template_type(type), /* TEXTURE DATA FORMAT */
600 num_channels,
601 destination_num_channels,
602 false /* Not a clear. */
603 };
604
605 /* Determine whether we can do direct BLIT or not. For compressed textures,
606 * always assume a direct blit (input data pretends to be float, but it is
607 * not). */
608 bool can_use_direct_blit = true;
609 if (!is_compressed && (expected_dst_bytes_per_pixel != input_bytes_per_pixel ||
610 num_channels != destination_num_channels))
611 {
612 can_use_direct_blit = false;
613 }
614
615 if (is_depth_format) {
616 if (type_ == GPU_TEXTURE_2D || type_ == GPU_TEXTURE_2D_ARRAY) {
617 /* Workaround for crash in validation layer when blitting to depth2D target with
618 * dimensions (1, 1, 1); */
619 if (extent[0] == 1 && extent[1] == 1 && extent[2] == 1 && totalsize == 4) {
620 can_use_direct_blit = false;
621 }
622 }
623 }
624
625 if (format_ == GPU_SRGB8_A8 && !can_use_direct_blit) {
627 "SRGB data upload does not work correctly using compute upload. "
628 "texname '%s'",
629 name_);
630 }
631
632 /* Safety Checks. */
633 if (type == GPU_DATA_UINT_24_8 || type == GPU_DATA_10_11_11_REV ||
634 type == GPU_DATA_2_10_10_10_REV || is_compressed)
635 {
636 BLI_assert(can_use_direct_blit &&
637 "Special input data type must be a 1-1 mapping with destination texture as it "
638 "cannot easily be split");
639 }
640
641 /* Debug and verification. */
642 if (!can_use_direct_blit) {
644 "gpu::MTLTexture::update_sub supplied bpp is %lu bytes (%d components per "
645 "pixel), but backing texture bpp is %lu bytes (%d components per pixel) "
646 "(TODO(Metal): Channel Conversion needed) (w: %d, h: %d, d: %d)",
647 input_bytes_per_pixel,
648 num_channels,
649 expected_dst_bytes_per_pixel,
650 destination_num_channels,
651 w_,
652 h_,
653 d_);
654
655 /* Check mip compatibility. */
656 if (mip != 0) {
658 "Updating texture layers other than mip=0 when data is mismatched is not "
659 "possible in METAL on macOS using texture->write\n");
660 return;
661 }
662
663 /* Check Format write-ability. */
664 if (mtl_format_get_writeable_view_format(destination_format) == MTLPixelFormatInvalid) {
666 "Updating texture -- destination MTLPixelFormat '%d' does not support write "
667 "operations, and no suitable TextureView format exists.\n",
668 *(int *)(&destination_format));
669 return;
670 }
671 }
672
673 /* Common Properties. */
674 MTLPixelFormat compatible_write_format = mtl_format_get_writeable_view_format(
675 destination_format);
676
677 /* Some texture formats are not writeable so we need to use a texture view. */
678 if (compatible_write_format == MTLPixelFormatInvalid) {
679 MTL_LOG_ERROR("Cannot use compute update blit with texture-view format: %d\n",
680 *((int *)&compatible_write_format));
681 return;
682 }
683
684 /* Fetch allocation from memory pool. */
686 totalsize, true, data);
687 id<MTLBuffer> staging_buffer = temp_allocation->get_metal_buffer();
688 BLI_assert(staging_buffer != nil);
689
690 /* Prepare command encoders. */
691 id<MTLBlitCommandEncoder> blit_encoder = nil;
692 id<MTLComputeCommandEncoder> compute_encoder = nil;
693 id<MTLTexture> staging_texture = nil;
694 id<MTLTexture> texture_handle = nil;
695
696 /* Use staging texture. */
697 bool use_staging_texture = false;
698
699 if (can_use_direct_blit) {
701 BLI_assert(blit_encoder != nil);
702
703 /* If we need to use a texture view to write texture data as the source
704 * format is unwritable, if our texture has not been initialized with
705 * texture view support, use a staging texture. */
706 if ((compatible_write_format != destination_format) &&
707 !(internal_gpu_image_usage_flags_ & GPU_TEXTURE_USAGE_FORMAT_VIEW))
708 {
709 use_staging_texture = true;
710 }
711 }
712 else {
713 compute_encoder = ctx->main_command_buffer.ensure_begin_compute_encoder();
714 BLI_assert(compute_encoder != nil);
715
716 /* For compute, we should use a stating texture to avoid texture write usage,
717 * if it has not been specified for the texture. Using shader-write disables
718 * lossless texture compression, so this is best to avoid where possible. */
719 if (!(internal_gpu_image_usage_flags_ & GPU_TEXTURE_USAGE_SHADER_WRITE)) {
720 use_staging_texture = true;
721 }
722 if (compatible_write_format != destination_format) {
723 if (!(internal_gpu_image_usage_flags_ & GPU_TEXTURE_USAGE_FORMAT_VIEW)) {
724 use_staging_texture = true;
725 }
726 }
727 }
728
729 /* Allocate stating texture if needed. */
730 if (use_staging_texture) {
731 /* Create staging texture to avoid shader-write limiting optimization. */
732 BLI_assert(texture_descriptor_ != nullptr);
733 MTLTextureUsage original_usage = texture_descriptor_.usage;
734 texture_descriptor_.usage = original_usage | MTLTextureUsageShaderWrite |
735 MTLTextureUsagePixelFormatView;
736 staging_texture = [ctx->device newTextureWithDescriptor:texture_descriptor_];
737 staging_texture.label = @"Staging texture";
738 texture_descriptor_.usage = original_usage;
739
740 /* Create texture view if needed. */
741 texture_handle = ((compatible_write_format == destination_format)) ?
742 [staging_texture retain] :
743 [staging_texture newTextureViewWithPixelFormat:compatible_write_format];
744 }
745 else {
746 /* Use texture view. */
747 if (compatible_write_format != destination_format) {
748 BLI_assert(internal_gpu_image_usage_flags_ & GPU_TEXTURE_USAGE_FORMAT_VIEW);
749 texture_handle = [texture_ newTextureViewWithPixelFormat:compatible_write_format];
750 }
751 else {
752 texture_handle = texture_;
753 [texture_handle retain];
754 }
755 }
756
757 switch (type_) {
758
759 /* 1D */
760 case GPU_TEXTURE_1D:
762 if (can_use_direct_blit) {
763 /* Use Blit based update. */
764 size_t bytes_per_row = expected_dst_bytes_per_pixel *
765 ((ctx->pipeline_state.unpack_row_length == 0) ?
766 extent[0] :
768 size_t bytes_per_image = bytes_per_row;
769 if (is_compressed) {
770 size_t block_size = to_block_size(format_);
771 size_t blocks_x = divide_ceil_u(extent[0], 4);
772 bytes_per_row = blocks_x * block_size;
773 bytes_per_image = bytes_per_row;
774 }
775 int max_array_index = ((type_ == GPU_TEXTURE_1D_ARRAY) ? extent[1] : 1);
776 for (int array_index = 0; array_index < max_array_index; array_index++) {
777
778 size_t buffer_array_offset = (bytes_per_image * (size_t)array_index);
779 [blit_encoder
780 copyFromBuffer:staging_buffer
781 sourceOffset:buffer_array_offset
782 sourceBytesPerRow:bytes_per_row
783 sourceBytesPerImage:bytes_per_image
784 sourceSize:MTLSizeMake(extent[0], 1, 1)
785 toTexture:texture_handle
786 destinationSlice:((type_ == GPU_TEXTURE_1D_ARRAY) ? (array_index + offset[1]) :
787 0)
788 destinationLevel:mip
789 destinationOrigin:MTLOriginMake(offset[0], 0, 0)];
790 }
791 }
792 else {
793 /* Use Compute Based update. */
794 if (type_ == GPU_TEXTURE_1D) {
795 id<MTLComputePipelineState> pso = texture_update_1d_get_kernel(
796 compute_specialization_kernel);
797 TextureUpdateParams params = {mip,
798 {extent[0], 1, 1},
799 {offset[0], 0, 0},
800 ((ctx->pipeline_state.unpack_row_length == 0) ?
801 extent[0] :
803
804 /* Bind resources via compute state for optimal state caching performance. */
806 cs.bind_pso(pso);
807 cs.bind_compute_bytes(&params, sizeof(params), 0);
808 cs.bind_compute_buffer(staging_buffer, 0, 1);
809 cs.bind_compute_texture(texture_handle, 0);
810 [compute_encoder
811 dispatchThreads:MTLSizeMake(extent[0], 1, 1) /* Width, Height, Layer */
812 threadsPerThreadgroup:MTLSizeMake(64, 1, 1)];
813 }
814 else if (type_ == GPU_TEXTURE_1D_ARRAY) {
815 id<MTLComputePipelineState> pso = texture_update_1d_array_get_kernel(
816 compute_specialization_kernel);
817 TextureUpdateParams params = {mip,
818 {extent[0], extent[1], 1},
819 {offset[0], offset[1], 0},
820 ((ctx->pipeline_state.unpack_row_length == 0) ?
821 extent[0] :
823
824 /* Bind resources via compute state for optimal state caching performance. */
826 cs.bind_pso(pso);
827 cs.bind_compute_bytes(&params, sizeof(params), 0);
828 cs.bind_compute_buffer(staging_buffer, 0, 1);
829 cs.bind_compute_texture(texture_handle, 0);
830 [compute_encoder
831 dispatchThreads:MTLSizeMake(extent[0], extent[1], 1) /* Width, layers, nil */
832 threadsPerThreadgroup:MTLSizeMake(8, 8, 1)];
833 }
834 }
835 } break;
836
837 /* 2D */
838 case GPU_TEXTURE_2D:
840 if (can_use_direct_blit) {
841 /* Use Blit encoder update. */
842 size_t bytes_per_row = expected_dst_bytes_per_pixel *
843 ((ctx->pipeline_state.unpack_row_length == 0) ?
844 extent[0] :
846 size_t bytes_per_image = bytes_per_row * extent[1];
847 if (is_compressed) {
848 size_t block_size = to_block_size(format_);
849 size_t blocks_x = divide_ceil_u(extent[0], 4);
850 size_t blocks_y = divide_ceil_u(extent[1], 4);
851 bytes_per_row = blocks_x * block_size;
852 bytes_per_image = bytes_per_row * blocks_y;
853 }
854
855 size_t texture_array_relative_offset = 0;
856 int base_slice = (type_ == GPU_TEXTURE_2D_ARRAY) ? offset[2] : 0;
857 int final_slice = base_slice + ((type_ == GPU_TEXTURE_2D_ARRAY) ? extent[2] : 1);
858
859 for (int array_slice = base_slice; array_slice < final_slice; array_slice++) {
860
861 if (array_slice > 0) {
863 BLI_assert(array_slice < d_);
864 }
865
866 [blit_encoder copyFromBuffer:staging_buffer
867 sourceOffset:texture_array_relative_offset
868 sourceBytesPerRow:bytes_per_row
869 sourceBytesPerImage:bytes_per_image
870 sourceSize:MTLSizeMake(extent[0], extent[1], 1)
871 toTexture:texture_handle
872 destinationSlice:array_slice
873 destinationLevel:mip
874 destinationOrigin:MTLOriginMake(offset[0], offset[1], 0)];
875
876 texture_array_relative_offset += bytes_per_image;
877 }
878 }
879 else {
880 /* Use Compute texture update. */
881 if (type_ == GPU_TEXTURE_2D) {
882 id<MTLComputePipelineState> pso = texture_update_2d_get_kernel(
883 compute_specialization_kernel);
884 TextureUpdateParams params = {mip,
885 {extent[0], extent[1], 1},
886 {offset[0], offset[1], 0},
887 ((ctx->pipeline_state.unpack_row_length == 0) ?
888 extent[0] :
890
891 /* Bind resources via compute state for optimal state caching performance. */
893 cs.bind_pso(pso);
894 cs.bind_compute_bytes(&params, sizeof(params), 0);
895 cs.bind_compute_buffer(staging_buffer, 0, 1);
896 cs.bind_compute_texture(texture_handle, 0);
897 [compute_encoder
898 dispatchThreads:MTLSizeMake(
899 extent[0], extent[1], 1) /* Width, Height, Layer */
900 threadsPerThreadgroup:MTLSizeMake(8, 8, 1)];
901 }
902 else if (type_ == GPU_TEXTURE_2D_ARRAY) {
903 id<MTLComputePipelineState> pso = texture_update_2d_array_get_kernel(
904 compute_specialization_kernel);
905 TextureUpdateParams params = {mip,
906 {extent[0], extent[1], extent[2]},
907 {offset[0], offset[1], offset[2]},
908 ((ctx->pipeline_state.unpack_row_length == 0) ?
909 extent[0] :
911
912 /* Bind resources via compute state for optimal state caching performance. */
914 cs.bind_pso(pso);
915 cs.bind_compute_bytes(&params, sizeof(params), 0);
916 cs.bind_compute_buffer(staging_buffer, 0, 1);
917 cs.bind_compute_texture(texture_handle, 0);
918 [compute_encoder dispatchThreads:MTLSizeMake(extent[0],
919 extent[1],
920 extent[2]) /* Width, Height, Layer */
921 threadsPerThreadgroup:MTLSizeMake(4, 4, 4)];
922 }
923 }
924
925 } break;
926
927 /* 3D */
928 case GPU_TEXTURE_3D: {
929 if (can_use_direct_blit) {
930 size_t bytes_per_row = expected_dst_bytes_per_pixel *
931 ((ctx->pipeline_state.unpack_row_length == 0) ?
932 extent[0] :
934 size_t bytes_per_image = bytes_per_row * extent[1];
935 [blit_encoder copyFromBuffer:staging_buffer
936 sourceOffset:0
937 sourceBytesPerRow:bytes_per_row
938 sourceBytesPerImage:bytes_per_image
939 sourceSize:MTLSizeMake(extent[0], extent[1], extent[2])
940 toTexture:texture_handle
941 destinationSlice:0
942 destinationLevel:mip
943 destinationOrigin:MTLOriginMake(offset[0], offset[1], offset[2])];
944 }
945 else {
946 id<MTLComputePipelineState> pso = texture_update_3d_get_kernel(
947 compute_specialization_kernel);
948 TextureUpdateParams params = {mip,
949 {extent[0], extent[1], extent[2]},
950 {offset[0], offset[1], offset[2]},
951 ((ctx->pipeline_state.unpack_row_length == 0) ?
952 extent[0] :
954
955 /* Bind resources via compute state for optimal state caching performance. */
957 cs.bind_pso(pso);
958 cs.bind_compute_bytes(&params, sizeof(params), 0);
959 cs.bind_compute_buffer(staging_buffer, 0, 1);
960 cs.bind_compute_texture(texture_handle, 0);
961 [compute_encoder
962 dispatchThreads:MTLSizeMake(
963 extent[0], extent[1], extent[2]) /* Width, Height, Depth */
964 threadsPerThreadgroup:MTLSizeMake(4, 4, 4)];
965 }
966 } break;
967
968 /* CUBE */
969 case GPU_TEXTURE_CUBE: {
970 if (can_use_direct_blit) {
971 size_t bytes_per_row = expected_dst_bytes_per_pixel *
972 ((ctx->pipeline_state.unpack_row_length == 0) ?
973 extent[0] :
975 size_t bytes_per_image = bytes_per_row * extent[1];
976 size_t texture_array_relative_offset = 0;
977
978 /* Iterate over all cube faces in range (offset[2], offset[2] + extent[2]). */
979 for (int i = 0; i < extent[2]; i++) {
980 int face_index = offset[2] + i;
981
982 [blit_encoder copyFromBuffer:staging_buffer
983 sourceOffset:texture_array_relative_offset
984 sourceBytesPerRow:bytes_per_row
985 sourceBytesPerImage:bytes_per_image
986 sourceSize:MTLSizeMake(extent[0], extent[1], 1)
987 toTexture:texture_handle
988 destinationSlice:face_index /* = cubeFace+arrayIndex*6 */
989 destinationLevel:mip
990 destinationOrigin:MTLOriginMake(offset[0], offset[1], 0)];
991 texture_array_relative_offset += bytes_per_image;
992 }
993 }
994 else {
996 "TODO(Metal): Support compute texture update for GPU_TEXTURE_CUBE %d, %d, %d\n",
997 w_,
998 h_,
999 d_);
1000 }
1001 } break;
1002
1004 if (can_use_direct_blit) {
1005
1006 size_t bytes_per_row = expected_dst_bytes_per_pixel *
1007 ((ctx->pipeline_state.unpack_row_length == 0) ?
1008 extent[0] :
1010 size_t bytes_per_image = bytes_per_row * extent[1];
1011
1012 /* Upload to all faces between offset[2] (which is zero in most cases) AND extent[2]. */
1013 size_t texture_array_relative_offset = 0;
1014 for (int i = 0; i < extent[2]; i++) {
1015 int face_index = offset[2] + i;
1016 [blit_encoder copyFromBuffer:staging_buffer
1017 sourceOffset:texture_array_relative_offset
1018 sourceBytesPerRow:bytes_per_row
1019 sourceBytesPerImage:bytes_per_image
1020 sourceSize:MTLSizeMake(extent[0], extent[1], 1)
1021 toTexture:texture_handle
1022 destinationSlice:face_index /* = cubeFace+arrayIndex*6. */
1023 destinationLevel:mip
1024 destinationOrigin:MTLOriginMake(offset[0], offset[1], 0)];
1025 texture_array_relative_offset += bytes_per_image;
1026 }
1027 }
1028 else {
1030 "TODO(Metal): Support compute texture update for GPU_TEXTURE_CUBE_ARRAY %d, %d, "
1031 "%d\n",
1032 w_,
1033 h_,
1034 d_);
1035 }
1036 } break;
1037
1038 case GPU_TEXTURE_BUFFER: {
1039 /* TODO(Metal): Support Data upload to TEXTURE BUFFER
1040 * Data uploads generally happen via VertBuf instead. */
1041 BLI_assert(false);
1042 } break;
1043
1044 case GPU_TEXTURE_ARRAY:
1045 /* Not an actual format - modifier flag for others. */
1046 return;
1047 }
1048
1049 /* If staging texture was used, copy contents to original texture. */
1050 if (use_staging_texture) {
1051 /* When using staging texture, copy results into existing texture. */
1052 BLI_assert(staging_texture != nil);
1053 blit_encoder = ctx->main_command_buffer.ensure_begin_blit_encoder();
1054
1055 /* Copy modified staging texture region back to original texture.
1056 * Differing blit dimensions based on type. */
1057 switch (type_) {
1058 case GPU_TEXTURE_1D:
1059 case GPU_TEXTURE_1D_ARRAY: {
1060 int base_slice = (type_ == GPU_TEXTURE_1D_ARRAY) ? offset[1] : 0;
1061 int final_slice = base_slice + ((type_ == GPU_TEXTURE_1D_ARRAY) ? extent[1] : 1);
1062 for (int array_index = base_slice; array_index < final_slice; array_index++) {
1063 [blit_encoder copyFromTexture:staging_texture
1064 sourceSlice:array_index
1065 sourceLevel:mip
1066 sourceOrigin:MTLOriginMake(offset[0], 0, 0)
1067 sourceSize:MTLSizeMake(extent[0], 1, 1)
1068 toTexture:texture_
1069 destinationSlice:array_index
1070 destinationLevel:mip
1071 destinationOrigin:MTLOriginMake(offset[0], 0, 0)];
1072 }
1073 } break;
1074 case GPU_TEXTURE_2D:
1075 case GPU_TEXTURE_2D_ARRAY: {
1076 int base_slice = (type_ == GPU_TEXTURE_2D_ARRAY) ? offset[2] : 0;
1077 int final_slice = base_slice + ((type_ == GPU_TEXTURE_2D_ARRAY) ? extent[2] : 1);
1078 for (int array_index = base_slice; array_index < final_slice; array_index++) {
1079 [blit_encoder copyFromTexture:staging_texture
1080 sourceSlice:array_index
1081 sourceLevel:mip
1082 sourceOrigin:MTLOriginMake(offset[0], offset[1], 0)
1083 sourceSize:MTLSizeMake(extent[0], extent[1], 1)
1084 toTexture:texture_
1085 destinationSlice:array_index
1086 destinationLevel:mip
1087 destinationOrigin:MTLOriginMake(offset[0], offset[1], 0)];
1088 }
1089 } break;
1090 case GPU_TEXTURE_3D: {
1091 [blit_encoder copyFromTexture:staging_texture
1092 sourceSlice:0
1093 sourceLevel:mip
1094 sourceOrigin:MTLOriginMake(offset[0], offset[1], offset[2])
1095 sourceSize:MTLSizeMake(extent[0], extent[1], extent[2])
1096 toTexture:texture_
1097 destinationSlice:0
1098 destinationLevel:mip
1099 destinationOrigin:MTLOriginMake(offset[0], offset[1], offset[2])];
1100 } break;
1101 case GPU_TEXTURE_CUBE:
1103 /* Iterate over all cube faces in range (offset[2], offset[2] + extent[2]). */
1104 for (int i = 0; i < extent[2]; i++) {
1105 int face_index = offset[2] + i;
1106 [blit_encoder copyFromTexture:staging_texture
1107 sourceSlice:face_index
1108 sourceLevel:mip
1109 sourceOrigin:MTLOriginMake(offset[0], offset[1], 0)
1110 sourceSize:MTLSizeMake(extent[0], extent[1], 1)
1111 toTexture:texture_
1112 destinationSlice:face_index
1113 destinationLevel:mip
1114 destinationOrigin:MTLOriginMake(offset[0], offset[1], 0)];
1115 }
1116 } break;
1117 case GPU_TEXTURE_ARRAY:
1118 case GPU_TEXTURE_BUFFER:
1120 break;
1121 }
1122
1123 [staging_texture release];
1124 }
1125
1126 /* Finalize Blit Encoder. */
1127 if (can_use_direct_blit) {
1128 /* Textures which use MTLStorageModeManaged need to have updated contents
1129 * synced back to CPU to avoid an automatic flush overwriting contents. */
1130 if (texture_.storageMode == MTLStorageModeManaged) {
1131 [blit_encoder synchronizeResource:texture_];
1132 }
1133 [blit_encoder optimizeContentsForGPUAccess:texture_];
1134 }
1135 else {
1136 /* Textures which use MTLStorageModeManaged need to have updated contents
1137 * synced back to CPU to avoid an automatic flush overwriting contents. */
1138 blit_encoder = ctx->main_command_buffer.ensure_begin_blit_encoder();
1139 if (texture_.storageMode == MTLStorageModeManaged) {
1140
1141 [blit_encoder synchronizeResource:texture_];
1142 }
1143 [blit_encoder optimizeContentsForGPUAccess:texture_];
1144 }
1145
1146 /* Decrement texture reference counts. This ensures temporary texture views are released. */
1147 [texture_handle release];
1148
1149 ctx->main_command_buffer.submit(false);
1150
1151 /* Release temporary staging buffer allocation.
1152 * NOTE: Allocation will be tracked with command submission and released once no longer in use.
1153 */
1154 temp_allocation->free();
1155 }
1156}
1157
1158void MTLTexture::update_sub(int offset[3],
1159 int extent[3],
1161 GPUPixelBuffer *pixbuf)
1162{
1163 /* Update texture from pixel buffer. */
1165 BLI_assert(pixbuf != nullptr);
1166
1167 /* Fetch pixel buffer metal buffer. */
1168 MTLPixelBuffer *mtl_pix_buf = static_cast<MTLPixelBuffer *>(unwrap(pixbuf));
1169 id<MTLBuffer> buffer = mtl_pix_buf->get_metal_buffer();
1170 BLI_assert(buffer != nil);
1171 if (buffer == nil) {
1172 return;
1173 }
1174
1175 /* Ensure texture is ready. */
1176 this->ensure_baked();
1177 BLI_assert(texture_ != nil);
1178
1179 /* Calculate dimensions. */
1180 int num_image_channels = to_component_len(format_);
1181
1182 size_t bits_per_pixel = num_image_channels * to_bytesize(format);
1183 size_t bytes_per_row = bits_per_pixel * extent[0];
1184 size_t bytes_per_image = bytes_per_row * extent[1];
1185
1186 /* Currently only required for 2D textures. */
1187 if (type_ == GPU_TEXTURE_2D) {
1188
1189 /* Create blit command encoder to copy data. */
1190 MTLContext *ctx = MTLContext::get();
1191 BLI_assert(ctx);
1192
1193 id<MTLBlitCommandEncoder> blit_encoder = ctx->main_command_buffer.ensure_begin_blit_encoder();
1194 [blit_encoder copyFromBuffer:buffer
1195 sourceOffset:0
1196 sourceBytesPerRow:bytes_per_row
1197 sourceBytesPerImage:bytes_per_image
1198 sourceSize:MTLSizeMake(extent[0], extent[1], 1)
1199 toTexture:texture_
1200 destinationSlice:0
1201 destinationLevel:0
1202 destinationOrigin:MTLOriginMake(offset[0], offset[1], 0)];
1203
1204 if (texture_.storageMode == MTLStorageModeManaged) {
1205 [blit_encoder synchronizeResource:texture_];
1206 }
1207 [blit_encoder optimizeContentsForGPUAccess:texture_];
1208 }
1209 else {
1210 BLI_assert(false);
1211 }
1212}
1213
1214void gpu::MTLTexture::ensure_mipmaps(int miplvl)
1215{
1216
1217 /* Do not update texture view. */
1218 BLI_assert(resource_mode_ != MTL_TEXTURE_MODE_TEXTURE_VIEW);
1219
1220 /* Clamp level to maximum. */
1221 int effective_h = (type_ == GPU_TEXTURE_1D_ARRAY) ? 0 : h_;
1222 int effective_d = (type_ != GPU_TEXTURE_3D) ? 0 : d_;
1223 int max_dimension = max_iii(w_, effective_h, effective_d);
1224 int max_miplvl = floor(log2(max_dimension));
1225 miplvl = min_ii(max_miplvl, miplvl);
1226
1227 /* Increase mipmap level. */
1228 if (mipmaps_ < miplvl) {
1229 mipmaps_ = miplvl;
1230
1231 /* Check if baked. */
1232 if (is_baked_ && mipmaps_ > mtl_max_mips_) {
1233 BLI_assert_msg(false,
1234 "Texture requires a higher mipmap level count. Please specify the required "
1235 "amount upfront.");
1236 is_dirty_ = true;
1237 MTL_LOG_WARNING("Texture requires regenerating due to increase in mip-count");
1238 }
1239 }
1240 this->mip_range_set(0, mipmaps_);
1241}
1242
1244{
1245 /* Compressed textures allow users to provide their own custom mipmaps. And
1246 * we can't generate them at runtime anyway. */
1247 if (format_flag_ & GPU_FORMAT_COMPRESSED) {
1248 return;
1249 }
1250
1251 /* Fetch Active Context. */
1252 MTLContext *ctx = MTLContext::get();
1253 BLI_assert(ctx);
1254
1255 if (!ctx->device) {
1256 MTL_LOG_ERROR("Cannot Generate mip-maps -- metal device invalid\n");
1257 BLI_assert(false);
1258 return;
1259 }
1260
1261 /* Ensure mipmaps. */
1262 this->ensure_mipmaps(mtl_max_mips_);
1263
1264 /* Ensure texture is baked. */
1265 this->ensure_baked();
1266 BLI_assert_msg(is_baked_ && texture_, "MTLTexture is not valid");
1267
1268 if (mipmaps_ == 1 || mtl_max_mips_ == 1) {
1269 MTL_LOG_WARNING("Call to generate mipmaps on texture with 'mipmaps_=1'");
1270 return;
1271 }
1272
1273 /* Verify if we can perform mipmap generation. */
1274 if (format_ == GPU_DEPTH_COMPONENT32F || format_ == GPU_DEPTH_COMPONENT24 ||
1275 format_ == GPU_DEPTH_COMPONENT16 || format_ == GPU_DEPTH32F_STENCIL8 ||
1276 format_ == GPU_DEPTH24_STENCIL8)
1277 {
1278 MTL_LOG_WARNING("Cannot generate mipmaps for textures using DEPTH formats");
1279 return;
1280 }
1281
1282 @autoreleasepool {
1283
1284 /* Fetch active BlitCommandEncoder. */
1285 id<MTLBlitCommandEncoder> enc = ctx->main_command_buffer.ensure_begin_blit_encoder();
1286 if (G.debug & G_DEBUG_GPU) {
1287 [enc insertDebugSignpost:@"Generate MipMaps"];
1288 }
1289 [enc generateMipmapsForTexture:texture_];
1290 has_generated_mips_ = true;
1291 }
1292 return;
1293}
1294
1296{
1297 /* Safety Checks. */
1298 gpu::MTLTexture *mt_src = this;
1299 gpu::MTLTexture *mt_dst = static_cast<gpu::MTLTexture *>(dst);
1300 BLI_assert((mt_dst->w_ == mt_src->w_) && (mt_dst->h_ == mt_src->h_) &&
1301 (mt_dst->d_ == mt_src->d_));
1302 BLI_assert(mt_dst->format_ == mt_src->format_);
1303 BLI_assert(mt_dst->type_ == mt_src->type_);
1304
1305 UNUSED_VARS_NDEBUG(mt_src);
1306
1307 /* Fetch active context. */
1308 MTLContext *ctx = MTLContext::get();
1309 BLI_assert(ctx);
1310
1311 /* Ensure texture is baked. */
1312 this->ensure_baked();
1313
1314 @autoreleasepool {
1315 /* Setup blit encoder. */
1316 id<MTLBlitCommandEncoder> blit_encoder = ctx->main_command_buffer.ensure_begin_blit_encoder();
1317 BLI_assert(blit_encoder != nil);
1318
1319 /* TODO(Metal): Consider supporting multiple mip levels IF the GL implementation
1320 * follows, currently it does not. */
1321 int mip = 0;
1322
1323 /* NOTE: mip_size_get() won't override any dimension that is equal to 0. */
1324 int extent[3] = {1, 1, 1};
1325 this->mip_size_get(mip, extent);
1326
1327 switch (mt_dst->type_) {
1330 case GPU_TEXTURE_3D: {
1331 /* Do full texture copy for 3D textures */
1332 BLI_assert(mt_dst->d_ == d_);
1333 [blit_encoder copyFromTexture:this->get_metal_handle_base()
1334 toTexture:mt_dst->get_metal_handle_base()];
1335 [blit_encoder optimizeContentsForGPUAccess:mt_dst->get_metal_handle_base()];
1336 } break;
1337 default: {
1338 int slice = 0;
1339 this->blit(blit_encoder,
1340 0,
1341 0,
1342 0,
1343 slice,
1344 mip,
1345 mt_dst,
1346 0,
1347 0,
1348 0,
1349 slice,
1350 mip,
1351 extent[0],
1352 extent[1],
1353 extent[2]);
1354 } break;
1355 }
1356 }
1357}
1358
1359void gpu::MTLTexture::clear(eGPUDataFormat data_format, const void *data)
1360{
1361 /* Ensure texture is baked. */
1362 this->ensure_baked();
1363
1364 /* If render-pass clear is not supported, use compute-based clear. */
1365 bool do_render_pass_clear = true;
1367 do_render_pass_clear = false;
1368 }
1369 /* If texture is buffer-backed, clear directly on buffer.
1370 * NOTE: This us currently only true for fallback atomic textures. */
1371 if (backing_buffer_ != nullptr) {
1372 uint num_channels = to_component_len(format_);
1373 bool fast_buf_clear_to_zero = true;
1374 const uint *val = reinterpret_cast<const uint *>(data);
1375 for (int i = 0; i < num_channels; i++) {
1376 fast_buf_clear_to_zero = fast_buf_clear_to_zero && (val[i] == 0);
1377 }
1378 if (fast_buf_clear_to_zero) {
1379 /* Fetch active context. */
1380 MTLContext *ctx = MTLContext::get();
1381 BLI_assert(ctx);
1382
1383 /* Begin compute encoder. */
1384 id<MTLBlitCommandEncoder> blit_encoder =
1386 [blit_encoder fillBuffer:backing_buffer_->get_metal_buffer()
1387 range:NSMakeRange(0, backing_buffer_->get_size())
1388 value:0];
1389 }
1390 else {
1391 BLI_assert_msg(false, "Non-zero buffer-backed texture clear not supported!");
1392 }
1393 return;
1394 }
1395
1396 if (do_render_pass_clear) {
1397 /* Create clear frame-buffer for fast clear. */
1398 GPUFrameBuffer *prev_fb = GPU_framebuffer_active_get();
1399 FrameBuffer *fb = unwrap(this->get_blit_framebuffer(-1, 0));
1400 fb->bind(true);
1401 fb->clear_attachment(this->attachment_type(0), data_format, data);
1402 GPU_framebuffer_bind(prev_fb);
1403 }
1404 else {
1406 /* Prepare specialization struct (For texture clear routine). */
1407 int num_channels = to_component_len(format_);
1408 TextureUpdateRoutineSpecialisation compute_specialization_kernel = {
1409 tex_data_format_to_msl_type_str(data_format), /* INPUT DATA FORMAT */
1410 tex_data_format_to_msl_texture_template_type(data_format), /* TEXTURE DATA FORMAT */
1411 num_channels,
1412 num_channels,
1413 true /* Operation is a clear. */
1414 };
1415
1416 /* Determine size of source data clear. */
1417 uint clear_data_size = to_bytesize(format_, data_format);
1418
1419 /* Fetch active context. */
1420 MTLContext *ctx = MTLContext::get();
1421 BLI_assert(ctx);
1422
1423 /* Determine writeable texture handle. */
1424 id<MTLTexture> texture_handle = texture_;
1425
1426 /* Begin compute encoder. */
1427 id<MTLComputeCommandEncoder> compute_encoder =
1429
1430 /* Perform clear operation based on texture type. */
1431 switch (type_) {
1432 case GPU_TEXTURE_1D: {
1433 id<MTLComputePipelineState> pso = texture_update_1d_get_kernel(
1434 compute_specialization_kernel);
1435 TextureUpdateParams params = {0,
1436 {w_, 1, 1},
1437 {0, 0, 0},
1438 ((ctx->pipeline_state.unpack_row_length == 0) ?
1439 w_ :
1441
1442 /* Bind resources via compute state for optimal state caching performance. */
1444 cs.bind_pso(pso);
1445 cs.bind_compute_bytes(&params, sizeof(params), 0);
1446 cs.bind_compute_bytes(data, clear_data_size, 1);
1447 cs.bind_compute_texture(texture_handle, 0);
1448 [compute_encoder dispatchThreads:MTLSizeMake(w_, 1, 1) /* Width, Height, Layer */
1449 threadsPerThreadgroup:MTLSizeMake(64, 1, 1)];
1450 } break;
1451 case GPU_TEXTURE_1D_ARRAY: {
1452 id<MTLComputePipelineState> pso = texture_update_1d_array_get_kernel(
1453 compute_specialization_kernel);
1454 TextureUpdateParams params = {0,
1455 {w_, h_, 1},
1456 {0, 0, 0},
1457 ((ctx->pipeline_state.unpack_row_length == 0) ?
1458 w_ :
1460
1461 /* Bind resources via compute state for optimal state caching performance. */
1463 cs.bind_pso(pso);
1464 cs.bind_compute_bytes(&params, sizeof(params), 0);
1465 cs.bind_compute_bytes(data, clear_data_size, 1);
1466 cs.bind_compute_texture(texture_handle, 0);
1467 [compute_encoder dispatchThreads:MTLSizeMake(w_, h_, 1) /* Width, layers, nil */
1468 threadsPerThreadgroup:MTLSizeMake(8, 8, 1)];
1469 } break;
1470 default: {
1472 "gpu::MTLTexture::clear requires compute pass for texture"
1473 "type: %d, but this is not yet supported",
1474 (int)type_);
1475 } break;
1476 }
1477
1478 /* Textures which use MTLStorageModeManaged need to have updated contents
1479 * synced back to CPU to avoid an automatic flush overwriting contents. */
1480 id<MTLBlitCommandEncoder> blit_encoder = ctx->main_command_buffer.ensure_begin_blit_encoder();
1481 if (texture_.storageMode == MTLStorageModeManaged) {
1482 [blit_encoder synchronizeResource:texture_];
1483 }
1484 [blit_encoder optimizeContentsForGPUAccess:texture_];
1485 }
1486}
1487static MTLTextureSwizzle swizzle_to_mtl(const char swizzle)
1488{
1489 switch (swizzle) {
1490 default:
1491 case 'x':
1492 case 'r':
1493 return MTLTextureSwizzleRed;
1494 case 'y':
1495 case 'g':
1496 return MTLTextureSwizzleGreen;
1497 case 'z':
1498 case 'b':
1499 return MTLTextureSwizzleBlue;
1500 case 'w':
1501 case 'a':
1502 return MTLTextureSwizzleAlpha;
1503 case '0':
1504 return MTLTextureSwizzleZero;
1505 case '1':
1506 return MTLTextureSwizzleOne;
1507 }
1508}
1509
1510void gpu::MTLTexture::swizzle_set(const char swizzle_mask[4])
1511{
1512 if (memcmp(tex_swizzle_mask_, swizzle_mask, 4) != 0) {
1513 memcpy(tex_swizzle_mask_, swizzle_mask, 4);
1514
1515 /* Creating the swizzle mask and flagging as dirty if changed. */
1516 MTLTextureSwizzleChannels new_swizzle_mask = MTLTextureSwizzleChannelsMake(
1517 swizzle_to_mtl(swizzle_mask[0]),
1518 swizzle_to_mtl(swizzle_mask[1]),
1519 swizzle_to_mtl(swizzle_mask[2]),
1520 swizzle_to_mtl(swizzle_mask[3]));
1521
1522 mtl_swizzle_mask_ = new_swizzle_mask;
1523 texture_view_dirty_flags_ |= TEXTURE_VIEW_SWIZZLE_DIRTY;
1524 }
1525}
1526
1528{
1529 BLI_assert(min <= max && min >= 0 && max <= mipmaps_);
1530
1531 /* NOTE:
1532 * - mip_min_ and mip_max_ are used to Clamp LODs during sampling.
1533 * - Given functions like Framebuffer::recursive_downsample modifies the mip range
1534 * between each layer, we do not want to be re-baking the texture.
1535 * - For the time being, we are going to just need to generate a FULL mipmap chain
1536 * as we do not know ahead of time whether mipmaps will be used.
1537 *
1538 * TODO(Metal): Add texture initialization flag to determine whether mipmaps are used
1539 * or not. Will be important for saving memory for big textures. */
1540 mip_min_ = min;
1541 mip_max_ = max;
1542
1543 if ((type_ == GPU_TEXTURE_1D || type_ == GPU_TEXTURE_1D_ARRAY || type_ == GPU_TEXTURE_BUFFER) &&
1544 max > 1)
1545 {
1546
1548 " MTLTexture of type TEXTURE_1D_ARRAY or TEXTURE_BUFFER cannot have a mipcount "
1549 "greater than 1\n");
1550 mip_min_ = 0;
1551 mip_max_ = 0;
1552 mipmaps_ = 0;
1553 BLI_assert(false);
1554 }
1555
1556 /* Mip range for texture view. */
1557 mip_texture_base_level_ = mip_min_;
1558 mip_texture_max_level_ = mip_max_;
1559 texture_view_dirty_flags_ |= TEXTURE_VIEW_MIP_DIRTY;
1560}
1561
1563{
1564 /* Prepare Array for return data. */
1565 BLI_assert(!(format_flag_ & GPU_FORMAT_COMPRESSED));
1566 BLI_assert(mip <= mipmaps_);
1567 BLI_assert(validate_data_format(format_, type));
1568
1569 /* NOTE: mip_size_get() won't override any dimension that is equal to 0. */
1570 int extent[3] = {1, 1, 1};
1571 this->mip_size_get(mip, extent);
1572
1573 size_t sample_len = extent[0] * max_ii(extent[1], 1) * max_ii(extent[2], 1);
1574 size_t sample_size = to_bytesize(format_, type);
1575 size_t texture_size = sample_len * sample_size;
1576 int num_channels = to_component_len(format_);
1577
1578 void *data = MEM_mallocN(texture_size + 8, "GPU_texture_read");
1579
1580 /* Ensure texture is baked. */
1581 if (is_baked_) {
1582 this->read_internal(
1583 mip, 0, 0, 0, extent[0], extent[1], extent[2], type, num_channels, texture_size + 8, data);
1584 }
1585 else {
1586 /* Clear return values? */
1587 MTL_LOG_WARNING("MTLTexture::read - reading from texture with no image data");
1588 }
1589
1590 return data;
1591}
1592
1593/* Fetch the raw buffer data from a texture and copy to CPU host ptr. */
1594void gpu::MTLTexture::read_internal(int mip,
1595 int x_off,
1596 int y_off,
1597 int z_off,
1598 int width,
1599 int height,
1600 int depth,
1601 eGPUDataFormat desired_output_format,
1602 int num_output_components,
1603 size_t debug_data_size,
1604 void *r_data)
1605{
1606 /* Verify textures are baked. */
1607 if (!is_baked_) {
1608 MTL_LOG_WARNING("gpu::MTLTexture::read_internal - Trying to read from a non-baked texture!");
1609 return;
1610 }
1611 /* Fetch active context. */
1612 MTLContext *ctx = MTLContext::get();
1613 BLI_assert(ctx);
1614
1615 /* Calculate Desired output size. */
1616 int num_channels = to_component_len(format_);
1617 BLI_assert(num_output_components <= num_channels);
1618 size_t desired_output_bpp = num_output_components * to_bytesize(desired_output_format);
1619
1620 /* Calculate Metal data output for trivial copy. */
1621 size_t image_bpp = get_mtl_format_bytesize(texture_.pixelFormat);
1622 uint image_components = get_mtl_format_num_components(texture_.pixelFormat);
1623 bool is_depth_format = (format_flag_ & GPU_FORMAT_DEPTH);
1624
1625 /* Verify if we need to use compute read. */
1626 eGPUDataFormat data_format = to_data_format(this->format_get());
1627 bool format_conversion_needed = (data_format != desired_output_format);
1628 bool can_use_simple_read = (desired_output_bpp == image_bpp) && (!format_conversion_needed) &&
1629 (num_output_components == image_components);
1630
1631 /* Depth must be read using the compute shader -- Some safety checks to verify that params are
1632 * correct. */
1633 if (is_depth_format) {
1634 can_use_simple_read = false;
1635 /* TODO(Metal): Stencil data write not yet supported, so force components to one. */
1636 image_components = 1;
1637 BLI_assert(num_output_components == 1);
1638 BLI_assert(image_components == 1);
1639 BLI_assert(data_format == GPU_DATA_FLOAT || data_format == GPU_DATA_UINT_24_8);
1640 BLI_assert(validate_data_format(format_, data_format));
1641 }
1642
1643 /* SPECIAL Workaround for R11G11B10, GPU_RGB10_A2, GPU_RGB10_A2UI textures requesting a read
1644 * using: GPU_DATA_10_11_11_REV. */
1645 if (desired_output_format == GPU_DATA_10_11_11_REV ||
1646 desired_output_format == GPU_DATA_2_10_10_10_REV)
1647 {
1648 BLI_assert(format_ == GPU_R11F_G11F_B10F || format_ == GPU_RGB10_A2 ||
1649 format_ == GPU_RGB10_A2UI);
1650
1651 /* override parameters - we'll be able to use simple copy, as bpp will match at 4 bytes. */
1652 image_bpp = sizeof(int);
1653 image_components = 1;
1654 desired_output_bpp = sizeof(int);
1655 num_output_components = 1;
1656
1657 data_format = GPU_DATA_INT;
1658 format_conversion_needed = false;
1659 can_use_simple_read = true;
1660 }
1661
1662 /* Determine size of output data. */
1663 size_t bytes_per_row = desired_output_bpp * width;
1664 size_t bytes_per_image = bytes_per_row * height;
1665 size_t total_bytes = bytes_per_image * max_ii(depth, 1);
1666
1667 if (can_use_simple_read) {
1668 /* DEBUG check that if direct copy is being used, then both the expected output size matches
1669 * the METAL texture size. */
1670 BLI_assert(
1671 ((num_output_components * to_bytesize(desired_output_format)) == desired_output_bpp) &&
1672 (desired_output_bpp == image_bpp));
1673 }
1674 /* DEBUG check that the allocated data size matches the bytes we expect. */
1675 BLI_assert(total_bytes <= debug_data_size);
1676 UNUSED_VARS_NDEBUG(debug_data_size);
1677
1678 /* Fetch allocation from scratch buffer. */
1679 gpu::MTLBuffer *dest_buf = MTLContext::get_global_memory_manager()->allocate_aligned(
1680 total_bytes, 256, true);
1681 BLI_assert(dest_buf != nullptr);
1682
1683 id<MTLBuffer> destination_buffer = dest_buf->get_metal_buffer();
1684 BLI_assert(destination_buffer != nil);
1685 void *destination_buffer_host_ptr = dest_buf->get_host_ptr();
1686 BLI_assert(destination_buffer_host_ptr != nullptr);
1687
1688 /* Prepare specialization struct (For non-trivial texture read routine). */
1689 int depth_format_mode = 0;
1690 if (is_depth_format) {
1691 depth_format_mode = 1;
1692 switch (desired_output_format) {
1693 case GPU_DATA_FLOAT:
1694 depth_format_mode = 1;
1695 break;
1696 case GPU_DATA_UINT_24_8:
1697 depth_format_mode = 2;
1698 break;
1699 case GPU_DATA_UINT:
1700 depth_format_mode = 4;
1701 break;
1702 default:
1703 BLI_assert_msg(false, "Unhandled depth read format case");
1704 break;
1705 }
1706 }
1707
1708 TextureReadRoutineSpecialisation compute_specialization_kernel = {
1709 tex_data_format_to_msl_texture_template_type(data_format), /* TEXTURE DATA TYPE */
1710 tex_data_format_to_msl_type_str(desired_output_format), /* OUTPUT DATA TYPE */
1711 num_channels, /* TEXTURE COMPONENT COUNT */
1712 num_output_components, /* OUTPUT DATA COMPONENT COUNT */
1713 depth_format_mode};
1714
1715 bool copy_successful = false;
1716 @autoreleasepool {
1717
1718 /* TODO(Metal): Verify whether we need some form of barrier here to ensure reads
1719 * happen after work with associated texture is finished. */
1720 GPU_finish();
1721
1723 id<MTLTexture> read_texture = texture_;
1724 /* Use texture-view handle if reading from a GPU texture view. */
1725 if (resource_mode_ == MTL_TEXTURE_MODE_TEXTURE_VIEW) {
1726 read_texture = this->get_metal_handle();
1727 }
1728 /* Create Texture View for SRGB special case to bypass internal type conversion. */
1729 if (format_ == GPU_SRGB8_A8) {
1730 BLI_assert(internal_gpu_image_usage_flags_ & GPU_TEXTURE_USAGE_FORMAT_VIEW);
1731 read_texture = [read_texture newTextureViewWithPixelFormat:MTLPixelFormatRGBA8Unorm];
1732 }
1733
1734 /* Perform per-texture type read. */
1735 switch (type_) {
1736 case GPU_TEXTURE_1D: {
1737 if (can_use_simple_read) {
1738 /* Use Blit Encoder READ. */
1739 id<MTLBlitCommandEncoder> enc = ctx->main_command_buffer.ensure_begin_blit_encoder();
1740 if (G.debug & G_DEBUG_GPU) {
1741 [enc insertDebugSignpost:@"GPUTextureRead1D"];
1742 }
1743 [enc copyFromTexture:read_texture
1744 sourceSlice:0
1745 sourceLevel:mip
1746 sourceOrigin:MTLOriginMake(x_off, 0, 0)
1747 sourceSize:MTLSizeMake(width, 1, 1)
1748 toBuffer:destination_buffer
1749 destinationOffset:0
1750 destinationBytesPerRow:bytes_per_row
1751 destinationBytesPerImage:bytes_per_image];
1752 copy_successful = true;
1753 }
1754 else {
1755
1756 /* Use Compute READ. */
1757 id<MTLComputeCommandEncoder> compute_encoder =
1758 ctx->main_command_buffer.ensure_begin_compute_encoder();
1759 id<MTLComputePipelineState> pso = texture_read_1d_get_kernel(
1760 compute_specialization_kernel);
1761 TextureReadParams params = {
1762 mip,
1763 {width, 1, 1},
1764 {x_off, 0, 0},
1765 };
1766
1767 /* Bind resources via compute state for optimal state caching performance. */
1768 MTLComputeState &cs = ctx->main_command_buffer.get_compute_state();
1769 cs.bind_pso(pso);
1770 cs.bind_compute_bytes(&params, sizeof(params), 0);
1771 cs.bind_compute_buffer(destination_buffer, 0, 1);
1772 cs.bind_compute_texture(read_texture, 0);
1773 [compute_encoder dispatchThreads:MTLSizeMake(width, 1, 1) /* Width, Height, Layer */
1774 threadsPerThreadgroup:MTLSizeMake(8, 8, 1)];
1775 copy_successful = true;
1776 }
1777 } break;
1778
1779 case GPU_TEXTURE_1D_ARRAY: {
1780 if (can_use_simple_read) {
1781 /* Use Blit Encoder READ. */
1782 id<MTLBlitCommandEncoder> enc = ctx->main_command_buffer.ensure_begin_blit_encoder();
1783 if (G.debug & G_DEBUG_GPU) {
1784 [enc insertDebugSignpost:@"GPUTextureRead1DArray"];
1785 }
1786
1787 int base_slice = y_off;
1788 int final_slice = base_slice + height;
1789 size_t texture_array_relative_offset = 0;
1790
1791 for (int array_slice = base_slice; array_slice < final_slice; array_slice++) {
1792 [enc copyFromTexture:read_texture
1793 sourceSlice:base_slice
1794 sourceLevel:mip
1795 sourceOrigin:MTLOriginMake(x_off, 0, 0)
1796 sourceSize:MTLSizeMake(width, 1, 1)
1797 toBuffer:destination_buffer
1798 destinationOffset:texture_array_relative_offset
1799 destinationBytesPerRow:bytes_per_row
1800 destinationBytesPerImage:bytes_per_row];
1801 texture_array_relative_offset += bytes_per_row;
1802 }
1803 copy_successful = true;
1804 }
1805 else {
1806 /* Use Compute READ. */
1807 id<MTLComputeCommandEncoder> compute_encoder =
1808 ctx->main_command_buffer.ensure_begin_compute_encoder();
1809 id<MTLComputePipelineState> pso = texture_read_1d_array_get_kernel(
1810 compute_specialization_kernel);
1811 TextureReadParams params = {
1812 mip,
1813 {width, height, 1},
1814 {x_off, y_off, 0},
1815 };
1816
1817 /* Bind resources via compute state for optimal state caching performance. */
1818 MTLComputeState &cs = ctx->main_command_buffer.get_compute_state();
1819 cs.bind_pso(pso);
1820 cs.bind_compute_bytes(&params, sizeof(params), 0);
1821 cs.bind_compute_buffer(destination_buffer, 0, 1);
1822 cs.bind_compute_texture(read_texture, 0);
1823 [compute_encoder dispatchThreads:MTLSizeMake(width, height, 1) /* Width, Height, Layer */
1824 threadsPerThreadgroup:MTLSizeMake(8, 8, 1)];
1825 copy_successful = true;
1826 }
1827 } break;
1828
1829 case GPU_TEXTURE_2D: {
1830 if (can_use_simple_read) {
1831 /* Use Blit Encoder READ. */
1832 id<MTLBlitCommandEncoder> enc = ctx->main_command_buffer.ensure_begin_blit_encoder();
1833 if (G.debug & G_DEBUG_GPU) {
1834 [enc insertDebugSignpost:@"GPUTextureRead2D"];
1835 }
1836 [enc copyFromTexture:read_texture
1837 sourceSlice:0
1838 sourceLevel:mip
1839 sourceOrigin:MTLOriginMake(x_off, y_off, 0)
1840 sourceSize:MTLSizeMake(width, height, 1)
1841 toBuffer:destination_buffer
1842 destinationOffset:0
1843 destinationBytesPerRow:bytes_per_row
1844 destinationBytesPerImage:bytes_per_image];
1845 copy_successful = true;
1846 }
1847 else {
1848
1849 /* Use Compute READ. */
1850 id<MTLComputeCommandEncoder> compute_encoder =
1851 ctx->main_command_buffer.ensure_begin_compute_encoder();
1852 id<MTLComputePipelineState> pso = texture_read_2d_get_kernel(
1853 compute_specialization_kernel);
1854 TextureReadParams params = {
1855 mip,
1856 {width, height, 1},
1857 {x_off, y_off, 0},
1858 };
1859
1860 /* Bind resources via compute state for optimal state caching performance. */
1861 MTLComputeState &cs = ctx->main_command_buffer.get_compute_state();
1862 cs.bind_pso(pso);
1863 cs.bind_compute_bytes(&params, sizeof(params), 0);
1864 cs.bind_compute_buffer(destination_buffer, 0, 1);
1865 cs.bind_compute_texture(read_texture, 0);
1866 [compute_encoder dispatchThreads:MTLSizeMake(width, height, 1) /* Width, Height, Layer */
1867 threadsPerThreadgroup:MTLSizeMake(8, 8, 1)];
1868 copy_successful = true;
1869 }
1870 } break;
1871
1872 case GPU_TEXTURE_2D_ARRAY: {
1873 if (can_use_simple_read) {
1874 /* Use Blit Encoder READ. */
1875 id<MTLBlitCommandEncoder> enc = ctx->main_command_buffer.ensure_begin_blit_encoder();
1876 if (G.debug & G_DEBUG_GPU) {
1877 [enc insertDebugSignpost:@"GPUTextureRead2DArray"];
1878 }
1879 int base_slice = z_off;
1880 int final_slice = base_slice + depth;
1881 size_t texture_array_relative_offset = 0;
1882
1883 for (int array_slice = base_slice; array_slice < final_slice; array_slice++) {
1884 [enc copyFromTexture:read_texture
1885 sourceSlice:array_slice
1886 sourceLevel:mip
1887 sourceOrigin:MTLOriginMake(x_off, y_off, 0)
1888 sourceSize:MTLSizeMake(width, height, 1)
1889 toBuffer:destination_buffer
1890 destinationOffset:texture_array_relative_offset
1891 destinationBytesPerRow:bytes_per_row
1892 destinationBytesPerImage:bytes_per_image];
1893 texture_array_relative_offset += bytes_per_image;
1894 }
1895 copy_successful = true;
1896 }
1897 else {
1898
1899 /* Use Compute READ */
1900 id<MTLComputeCommandEncoder> compute_encoder =
1901 ctx->main_command_buffer.ensure_begin_compute_encoder();
1902 id<MTLComputePipelineState> pso = texture_read_2d_array_get_kernel(
1903 compute_specialization_kernel);
1904 TextureReadParams params = {
1905 mip,
1906 {width, height, depth},
1907 {x_off, y_off, z_off},
1908 };
1909
1910 /* Bind resources via compute state for optimal state caching performance. */
1911 MTLComputeState &cs = ctx->main_command_buffer.get_compute_state();
1912 cs.bind_pso(pso);
1913 cs.bind_compute_bytes(&params, sizeof(params), 0);
1914 cs.bind_compute_buffer(destination_buffer, 0, 1);
1915 cs.bind_compute_texture(read_texture, 0);
1916 [compute_encoder
1917 dispatchThreads:MTLSizeMake(width, height, depth) /* Width, Height, Layer */
1918 threadsPerThreadgroup:MTLSizeMake(8, 8, 1)];
1919 copy_successful = true;
1920 }
1921 } break;
1922
1923 case GPU_TEXTURE_3D: {
1924 if (can_use_simple_read) {
1925 /* Use Blit Encoder READ. */
1926 id<MTLBlitCommandEncoder> enc = ctx->main_command_buffer.ensure_begin_blit_encoder();
1927 if (G.debug & G_DEBUG_GPU) {
1928 [enc insertDebugSignpost:@"GPUTextureRead3D"];
1929 }
1930 [enc copyFromTexture:read_texture
1931 sourceSlice:0
1932 sourceLevel:mip
1933 sourceOrigin:MTLOriginMake(x_off, y_off, z_off)
1934 sourceSize:MTLSizeMake(width, height, depth)
1935 toBuffer:destination_buffer
1936 destinationOffset:0
1937 destinationBytesPerRow:bytes_per_row
1938 destinationBytesPerImage:bytes_per_image];
1939 copy_successful = true;
1940 }
1941 else {
1942
1943 /* Use Compute READ. */
1944 id<MTLComputeCommandEncoder> compute_encoder =
1945 ctx->main_command_buffer.ensure_begin_compute_encoder();
1946 id<MTLComputePipelineState> pso = texture_read_3d_get_kernel(
1947 compute_specialization_kernel);
1948 TextureReadParams params = {
1949 mip,
1950 {width, height, depth},
1951 {x_off, y_off, z_off},
1952 };
1953
1954 /* Bind resources via compute state for optimal state caching performance. */
1955 MTLComputeState &cs = ctx->main_command_buffer.get_compute_state();
1956 cs.bind_pso(pso);
1957 cs.bind_compute_bytes(&params, sizeof(params), 0);
1958 cs.bind_compute_buffer(destination_buffer, 0, 1);
1959 cs.bind_compute_texture(read_texture, 0);
1960 [compute_encoder
1961 dispatchThreads:MTLSizeMake(width, height, depth) /* Width, Height, Layer */
1962 threadsPerThreadgroup:MTLSizeMake(4, 4, 4)];
1963 copy_successful = true;
1964 }
1965 } break;
1966
1967 case GPU_TEXTURE_CUBE:
1969 BLI_assert_msg(z_off == 0 || type_ == GPU_TEXTURE_CUBE_ARRAY,
1970 "z_off > 0 is only supported by TEXTURE CUBE ARRAY reads.");
1971 BLI_assert_msg(depth <= 6 || type_ == GPU_TEXTURE_CUBE_ARRAY,
1972 "depth > 6 is only supported by TEXTURE CUBE ARRAY reads. ");
1973 if (can_use_simple_read) {
1974 id<MTLBlitCommandEncoder> enc = ctx->main_command_buffer.ensure_begin_blit_encoder();
1975 if (G.debug & G_DEBUG_GPU) {
1976 [enc insertDebugSignpost:@"GPUTextureReadCubeArray"];
1977 }
1978
1979 /* NOTE: Depth should have a minimum value of 1 as we read at least one slice. */
1980 int base_slice = z_off;
1981 int final_slice = base_slice + depth;
1982 size_t texture_array_relative_offset = 0;
1983
1984 for (int array_slice = base_slice; array_slice < final_slice; array_slice++) {
1985 [enc copyFromTexture:read_texture
1986 sourceSlice:array_slice
1987 sourceLevel:mip
1988 sourceOrigin:MTLOriginMake(x_off, y_off, 0)
1989 sourceSize:MTLSizeMake(width, height, 1)
1990 toBuffer:destination_buffer
1991 destinationOffset:texture_array_relative_offset
1992 destinationBytesPerRow:bytes_per_row
1993 destinationBytesPerImage:bytes_per_image];
1994
1995 texture_array_relative_offset += bytes_per_image;
1996 }
1997 MTL_LOG_INFO("Copying texture data to buffer GPU_TEXTURE_CUBE_ARRAY");
1998 copy_successful = true;
1999 }
2000 else {
2001 MTL_LOG_ERROR("TODO(Metal): unsupported compute copy of texture cube array");
2002 }
2003 } break;
2004
2005 default:
2007 "gpu::MTLTexture::read_internal simple-copy not yet supported for texture "
2008 "type: %d",
2009 (int)type_);
2010 break;
2011 }
2012
2013 if (copy_successful) {
2014
2015 /* Use Blit encoder to synchronize results back to CPU. */
2016 if (dest_buf->get_resource_options() == MTLResourceStorageModeManaged) {
2017 id<MTLBlitCommandEncoder> enc = ctx->main_command_buffer.ensure_begin_blit_encoder();
2018 if (G.debug & G_DEBUG_GPU) {
2019 [enc insertDebugSignpost:@"GPUTextureRead-syncResource"];
2020 }
2021 [enc synchronizeResource:destination_buffer];
2022 }
2023
2024 /* Ensure GPU copy commands have completed. */
2025 GPU_finish();
2026
2027 /* Copy data from Shared Memory into ptr. */
2028 memcpy(r_data, destination_buffer_host_ptr, total_bytes);
2029 MTL_LOG_INFO("gpu::MTLTexture::read_internal success! %lu bytes read", total_bytes);
2030 }
2031 else {
2033 "gpu::MTLTexture::read_internal not yet supported for this config -- data "
2034 "format different (src %lu bytes, dst %lu bytes) (src format: %d, dst format: %d), or "
2035 "varying component counts (src %d, dst %d)",
2036 image_bpp,
2037 desired_output_bpp,
2038 (int)data_format,
2039 (int)desired_output_format,
2040 image_components,
2041 num_output_components);
2042 }
2043
2044 /* Release destination buffer. */
2045 dest_buf->free();
2046 }
2047}
2048
2049/* Remove once no longer required -- will just return 0 for now in MTL path. */
2051{
2052 return 0;
2053}
2054
2056{
2057 this->prepare_internal();
2058 /* TODO(jbakker): Other limit checks should be added as well. When a texture violates a limit it
2059 * is not backed by a texture and will crash when used. */
2060 const int limit = GPU_max_texture_3d_size();
2061 if ((type_ == GPU_TEXTURE_3D) && (w_ > limit || h_ > limit || d_ > limit)) {
2062 return false;
2063 }
2064 return true;
2065}
2066
2068{
2069 MTLPixelFormat mtl_format = gpu_texture_format_to_metal(this->format_);
2070 mtl_max_mips_ = 1;
2071 mipmaps_ = 0;
2072 this->mip_range_set(0, 0);
2073
2074 /* Create texture from VertBuf's buffer. */
2075 MTLVertBuf *mtl_vbo = static_cast<MTLVertBuf *>(vbo);
2076 mtl_vbo->bind();
2077 mtl_vbo->flag_used();
2078
2079 /* Get Metal Buffer. */
2080 id<MTLBuffer> source_buffer = mtl_vbo->get_metal_buffer();
2081 BLI_assert(source_buffer);
2082
2083 /* Verify size. */
2084 if (w_ <= 0) {
2085 MTL_LOG_WARNING("Allocating texture buffer of width 0!");
2086 w_ = 1;
2087 }
2088
2089 /* Verify Texture and vertex buffer alignment. */
2091 size_t bytes_per_pixel = get_mtl_format_bytesize(mtl_format);
2092 size_t bytes_per_row = bytes_per_pixel * w_;
2093
2094 MTLContext *mtl_ctx = MTLContext::get();
2095 uint32_t align_requirement = static_cast<uint32_t>(
2096 [mtl_ctx->device minimumLinearTextureAlignmentForPixelFormat:mtl_format]);
2097
2098 /* If stride is larger than bytes per pixel, but format has multiple attributes,
2099 * split attributes across several pixels. */
2100 if (format->stride > bytes_per_pixel && format->attr_len > 1) {
2101
2102 /* We need to increase the number of pixels available to store additional attributes.
2103 * First ensure that the total stride of the vertex format fits uniformly into
2104 * multiple pixels. If these sizes are different, then attributes are of differing
2105 * sizes and this operation is unsupported. */
2106 if (bytes_per_pixel * format->attr_len != format->stride) {
2107 BLI_assert_msg(false,
2108 "Cannot split attributes across multiple pixels as attribute format sizes do "
2109 "not match.");
2110 return false;
2111 }
2112
2113 /* Provide a single pixel per attribute. */
2114 /* Increase bytes per row to ensure there are enough bytes for all vertex attribute data. */
2115 bytes_per_row *= format->attr_len;
2116 BLI_assert(bytes_per_row == format->stride * w_);
2117
2118 /* Multiply width of image to provide one attribute per pixel. */
2119 w_ *= format->attr_len;
2120 BLI_assert(bytes_per_row == bytes_per_pixel * w_);
2121 BLI_assert_msg(w_ == mtl_vbo->vertex_len * format->attr_len,
2122 "Image should contain one pixel for each attribute in every vertex.");
2123 }
2124 else {
2125 /* Verify per-vertex size aligns with texture size. */
2126 BLI_assert(bytes_per_pixel == format->stride &&
2127 "Pixel format stride MUST match the texture format stride -- These being different "
2128 "is likely caused by Metal's VBO padding to a minimum of 4-bytes per-vertex."
2129 " If multiple attributes are used. Each attribute is to be packed into its own "
2130 "individual pixel when stride length is exceeded. ");
2131 }
2132
2133 /* Create texture descriptor. */
2135 texture_descriptor_ = [[MTLTextureDescriptor alloc] init];
2136 texture_descriptor_.pixelFormat = mtl_format;
2137 texture_descriptor_.textureType = MTLTextureTypeTextureBuffer;
2138 texture_descriptor_.width = w_;
2139 texture_descriptor_.height = 1;
2140 texture_descriptor_.depth = 1;
2141 texture_descriptor_.arrayLength = 1;
2142 texture_descriptor_.mipmapLevelCount = mtl_max_mips_;
2143 texture_descriptor_.usage =
2144 MTLTextureUsageShaderRead | MTLTextureUsageShaderWrite |
2145 MTLTextureUsagePixelFormatView; /* TODO(Metal): Optimize usage flags. */
2146 texture_descriptor_.storageMode = [source_buffer storageMode];
2147 texture_descriptor_.sampleCount = 1;
2148 texture_descriptor_.cpuCacheMode = [source_buffer cpuCacheMode];
2149 texture_descriptor_.hazardTrackingMode = [source_buffer hazardTrackingMode];
2150
2151 texture_ = [source_buffer
2152 newTextureWithDescriptor:texture_descriptor_
2153 offset:0
2154 bytesPerRow:ceil_to_multiple_ul(bytes_per_row, align_requirement)];
2155 aligned_w_ = bytes_per_row / bytes_per_pixel;
2156
2157 BLI_assert(texture_);
2158 texture_.label = [NSString stringWithUTF8String:this->get_name()];
2159 is_baked_ = true;
2160 is_dirty_ = false;
2161 resource_mode_ = MTL_TEXTURE_MODE_VBO;
2162
2163 /* Track Status. */
2164 vert_buffer_ = mtl_vbo;
2165 vert_buffer_mtl_ = source_buffer;
2166
2167 return true;
2168}
2169
2171 int mip_offset,
2172 int layer_offset,
2173 bool use_stencil)
2174{
2175 BLI_assert(src);
2176
2177 /* Zero initialize. */
2178 this->prepare_internal();
2179
2180 /* Flag as using texture view. */
2181 resource_mode_ = MTL_TEXTURE_MODE_TEXTURE_VIEW;
2182 source_texture_ = src;
2183 mip_texture_base_level_ = mip_offset;
2184 mip_texture_base_layer_ = layer_offset;
2185 texture_view_dirty_flags_ |= TEXTURE_VIEW_MIP_DIRTY;
2186
2187 /* Assign usage. */
2188 internal_gpu_image_usage_flags_ = GPU_texture_usage(src);
2189
2190 /* Assign texture as view. */
2191 gpu::MTLTexture *mtltex = static_cast<gpu::MTLTexture *>(unwrap(src));
2192 mtltex->ensure_baked();
2193 texture_ = mtltex->texture_;
2194 BLI_assert(texture_);
2195 [texture_ retain];
2196
2197 /* Flag texture as baked -- we do not need explicit initialization. */
2198 is_baked_ = true;
2199 is_dirty_ = false;
2200
2201 /* Stencil view support. */
2202 texture_view_stencil_ = false;
2203 if (use_stencil) {
2205 texture_view_stencil_ = true;
2206 }
2207
2208 /* Bake mip swizzle view. */
2209 bake_mip_swizzle_view();
2210 return true;
2211}
2212
2215/* -------------------------------------------------------------------- */
2220{
2221 return is_baked_;
2222}
2223
2224/* Prepare texture parameters after initialization, but before baking. */
2225void gpu::MTLTexture::prepare_internal()
2226{
2227 /* Take a copy of the flags so that any modifications we make won't effect the texture
2228 * cache/pool match finding test. */
2229 internal_gpu_image_usage_flags_ = gpu_image_usage_flags_;
2230
2231 /* Metal: Texture clearing is done using frame-buffer clear. This has no performance impact or
2232 * bandwidth implications for lossless compression and is considered best-practice.
2233 *
2234 * Attachment usage also required for depth-stencil attachment targets, for depth-update support.
2235 * NOTE: Emulated atomic textures cannot support render-target usage. For clearing, the backing
2236 * buffer is cleared instead.
2237 */
2238 if (!((internal_gpu_image_usage_flags_ & GPU_TEXTURE_USAGE_ATOMIC) &&
2239 !MTLBackend::get_capabilities().supports_texture_atomics))
2240 {
2241 /* Force attachment usage - see comment above. */
2242 internal_gpu_image_usage_flags_ |= GPU_TEXTURE_USAGE_ATTACHMENT;
2243 }
2244
2245 /* Derive maximum number of mip levels by default.
2246 * TODO(Metal): This can be removed if max mip counts are specified upfront. */
2247 if (type_ == GPU_TEXTURE_1D || type_ == GPU_TEXTURE_1D_ARRAY || type_ == GPU_TEXTURE_BUFFER) {
2248 mtl_max_mips_ = 1;
2249 }
2250 else {
2251 /* Require correct explicit mipmap level counts. */
2252 mtl_max_mips_ = mipmaps_;
2253 }
2254}
2255
2256void gpu::MTLTexture::ensure_baked()
2257{
2258
2259 /* If properties have changed, re-bake. */
2260 id<MTLTexture> previous_texture = nil;
2261 bool copy_previous_contents = false;
2262
2263 if (is_baked_ && is_dirty_) {
2264 copy_previous_contents = true;
2265 previous_texture = texture_;
2266 [previous_texture retain];
2267 this->reset();
2268 }
2269
2270 if (!is_baked_) {
2271 MTLContext *ctx = MTLContext::get();
2272 BLI_assert(ctx);
2273
2274 /* Ensure texture mode is valid. */
2275 BLI_assert(resource_mode_ != MTL_TEXTURE_MODE_EXTERNAL);
2276 BLI_assert(resource_mode_ != MTL_TEXTURE_MODE_TEXTURE_VIEW);
2277 BLI_assert(resource_mode_ != MTL_TEXTURE_MODE_VBO);
2278
2279 /* Format and mip levels (TODO(Metal): Optimize mipmaps counts, specify up-front). */
2280 MTLPixelFormat mtl_format = gpu_texture_format_to_metal(format_);
2281
2282 /* SRGB textures require a texture view for reading data and when rendering with SRGB
2283 * disabled. Enabling the texture_view or texture_read usage flags disables lossless
2284 * compression, so the situations in which it is used should be limited. */
2285 if (format_ == GPU_SRGB8_A8) {
2286 internal_gpu_image_usage_flags_ |= GPU_TEXTURE_USAGE_FORMAT_VIEW;
2287 }
2288
2289 /* Create texture descriptor. */
2290 switch (type_) {
2291
2292 /* 1D */
2293 case GPU_TEXTURE_1D:
2294 case GPU_TEXTURE_1D_ARRAY: {
2295 BLI_assert(w_ > 0);
2296 texture_descriptor_ = [[MTLTextureDescriptor alloc] init];
2297 texture_descriptor_.pixelFormat = mtl_format;
2298 texture_descriptor_.textureType = (type_ == GPU_TEXTURE_1D_ARRAY) ? MTLTextureType1DArray :
2299 MTLTextureType1D;
2300 texture_descriptor_.width = w_;
2301 texture_descriptor_.height = 1;
2302 texture_descriptor_.depth = 1;
2303 texture_descriptor_.arrayLength = (type_ == GPU_TEXTURE_1D_ARRAY) ? h_ : 1;
2304 texture_descriptor_.mipmapLevelCount = (mtl_max_mips_ > 0) ? mtl_max_mips_ : 1;
2305 texture_descriptor_.usage = mtl_usage_from_gpu(internal_gpu_image_usage_flags_);
2306 texture_descriptor_.storageMode = MTLStorageModePrivate;
2307 texture_descriptor_.sampleCount = 1;
2308 texture_descriptor_.cpuCacheMode = MTLCPUCacheModeDefaultCache;
2309 texture_descriptor_.hazardTrackingMode = MTLHazardTrackingModeDefault;
2310 } break;
2311
2312 /* 2D */
2313 case GPU_TEXTURE_2D:
2314 case GPU_TEXTURE_2D_ARRAY: {
2315 BLI_assert(w_ > 0 && h_ > 0);
2316 texture_descriptor_ = [[MTLTextureDescriptor alloc] init];
2317 texture_descriptor_.pixelFormat = mtl_format;
2318 texture_descriptor_.textureType = (type_ == GPU_TEXTURE_2D_ARRAY) ? MTLTextureType2DArray :
2319 MTLTextureType2D;
2320 texture_descriptor_.width = w_;
2321 texture_descriptor_.height = h_;
2322 texture_descriptor_.depth = 1;
2323 texture_descriptor_.arrayLength = (type_ == GPU_TEXTURE_2D_ARRAY) ? d_ : 1;
2324 texture_descriptor_.mipmapLevelCount = (mtl_max_mips_ > 0) ? mtl_max_mips_ : 1;
2325 texture_descriptor_.usage = mtl_usage_from_gpu(internal_gpu_image_usage_flags_);
2326 texture_descriptor_.storageMode = MTLStorageModePrivate;
2327 texture_descriptor_.sampleCount = 1;
2328 texture_descriptor_.cpuCacheMode = MTLCPUCacheModeDefaultCache;
2329 texture_descriptor_.hazardTrackingMode = MTLHazardTrackingModeDefault;
2330 } break;
2331
2332 /* 3D */
2333 case GPU_TEXTURE_3D: {
2334 BLI_assert(w_ > 0 && h_ > 0 && d_ > 0);
2335 texture_descriptor_ = [[MTLTextureDescriptor alloc] init];
2336 texture_descriptor_.pixelFormat = mtl_format;
2337 texture_descriptor_.textureType = MTLTextureType3D;
2338 texture_descriptor_.width = w_;
2339 texture_descriptor_.height = h_;
2340 texture_descriptor_.depth = d_;
2341 texture_descriptor_.arrayLength = 1;
2342 texture_descriptor_.mipmapLevelCount = (mtl_max_mips_ > 0) ? mtl_max_mips_ : 1;
2343 texture_descriptor_.usage = mtl_usage_from_gpu(internal_gpu_image_usage_flags_);
2344 texture_descriptor_.storageMode = MTLStorageModePrivate;
2345 texture_descriptor_.sampleCount = 1;
2346 texture_descriptor_.cpuCacheMode = MTLCPUCacheModeDefaultCache;
2347 texture_descriptor_.hazardTrackingMode = MTLHazardTrackingModeDefault;
2348 } break;
2349
2350 /* CUBE TEXTURES */
2351 case GPU_TEXTURE_CUBE:
2353 /* NOTE: For a cube-map 'Texture::d_' refers to total number of faces,
2354 * not just array slices. */
2355 BLI_assert(w_ > 0 && h_ > 0);
2356 texture_descriptor_ = [[MTLTextureDescriptor alloc] init];
2357 texture_descriptor_.pixelFormat = mtl_format;
2358 texture_descriptor_.textureType = (type_ == GPU_TEXTURE_CUBE_ARRAY) ?
2359 MTLTextureTypeCubeArray :
2360 MTLTextureTypeCube;
2361 texture_descriptor_.width = w_;
2362 texture_descriptor_.height = h_;
2363 texture_descriptor_.depth = 1;
2364 texture_descriptor_.arrayLength = (type_ == GPU_TEXTURE_CUBE_ARRAY) ? d_ / 6 : 1;
2365 texture_descriptor_.mipmapLevelCount = (mtl_max_mips_ > 0) ? mtl_max_mips_ : 1;
2366 texture_descriptor_.usage = mtl_usage_from_gpu(internal_gpu_image_usage_flags_);
2367 texture_descriptor_.storageMode = MTLStorageModePrivate;
2368 texture_descriptor_.sampleCount = 1;
2369 texture_descriptor_.cpuCacheMode = MTLCPUCacheModeDefaultCache;
2370 texture_descriptor_.hazardTrackingMode = MTLHazardTrackingModeDefault;
2371 } break;
2372
2373 /* GPU_TEXTURE_BUFFER */
2374 case GPU_TEXTURE_BUFFER: {
2375 texture_descriptor_ = [[MTLTextureDescriptor alloc] init];
2376 texture_descriptor_.pixelFormat = mtl_format;
2377 texture_descriptor_.textureType = MTLTextureTypeTextureBuffer;
2378 texture_descriptor_.width = w_;
2379 texture_descriptor_.height = 1;
2380 texture_descriptor_.depth = 1;
2381 texture_descriptor_.arrayLength = 1;
2382 texture_descriptor_.mipmapLevelCount = (mtl_max_mips_ > 0) ? mtl_max_mips_ : 1;
2383 texture_descriptor_.usage = mtl_usage_from_gpu(internal_gpu_image_usage_flags_);
2384 texture_descriptor_.storageMode = MTLStorageModePrivate;
2385 texture_descriptor_.sampleCount = 1;
2386 texture_descriptor_.cpuCacheMode = MTLCPUCacheModeDefaultCache;
2387 texture_descriptor_.hazardTrackingMode = MTLHazardTrackingModeDefault;
2388 } break;
2389
2390 default: {
2391 MTL_LOG_ERROR("[METAL] Error: Cannot create texture with unknown type: %d\n", type_);
2392 return;
2393 } break;
2394 }
2395
2396 /* Determine Resource Mode. */
2397 resource_mode_ = MTL_TEXTURE_MODE_DEFAULT;
2398
2399 /* Override storage mode if memoryless attachments are being used.
2400 * NOTE: Memoryless textures can only be supported on TBDR GPUs. */
2401 if (internal_gpu_image_usage_flags_ & GPU_TEXTURE_USAGE_MEMORYLESS) {
2402 const bool is_tile_based_arch = (GPU_platform_architecture() == GPU_ARCHITECTURE_TBDR);
2403 if (is_tile_based_arch) {
2404 texture_descriptor_.storageMode = MTLStorageModeMemoryless;
2405 }
2406 }
2407
2412 bool native_texture_atomics = MTLBackend::get_capabilities().supports_texture_atomics;
2413 if ((internal_gpu_image_usage_flags_ & GPU_TEXTURE_USAGE_ATOMIC) && !native_texture_atomics) {
2414
2415 /* Validate format support. */
2417 "Texture atomic fallback support is only available for GPU_TEXTURE_2D, "
2418 "GPU_TEXTURE_2D_ARRAY and GPU_TEXTURE_3D.");
2419
2420 /* Re-assign 2D resolution to encompass all texture layers.
2421 * Texture access is handled by remapping to a linear ID and using this in the destination
2422 * texture. based on original with: LinearPxID = x + y*layer_w + z*(layer_h*layer_w);
2423 * tx_2d.y = LinearPxID/2D_tex_width;
2424 * tx_2d.x = LinearPxID - (tx_2d.y*2D_tex_width); */
2426 /* Maximum 2D texture dimensions will be (16384, 16384) on all target platforms. */
2427 const uint max_width = 16384;
2428 const uint max_height = 16384;
2429 const uint pixels_res = w_ * h_ * d_;
2430
2431 uint new_w = 0, new_h = 0;
2432 if (pixels_res <= max_width) {
2433 new_w = pixels_res;
2434 new_h = 1;
2435 }
2436 else {
2437 new_w = max_width;
2438 new_h = ((pixels_res % new_w) == 0) ? (pixels_res / new_w) : ((pixels_res / new_w) + 1);
2439 }
2440
2441 texture_descriptor_.width = new_w;
2442 texture_descriptor_.height = new_h;
2443
2444 UNUSED_VARS_NDEBUG(max_height);
2445 BLI_assert_msg(texture_descriptor_.width <= max_width &&
2446 texture_descriptor_.height <= max_height,
2447 "Atomic fallback support texture is too large.");
2448 }
2449
2450 /* Allocate buffer for texture data. */
2451 size_t bytes_per_pixel = get_mtl_format_bytesize(mtl_format);
2452 size_t bytes_per_row = bytes_per_pixel * texture_descriptor_.width;
2453 size_t total_bytes = bytes_per_row * texture_descriptor_.height;
2454
2456 total_bytes, (internal_gpu_image_usage_flags_ & GPU_TEXTURE_USAGE_HOST_READ));
2457 BLI_assert(backing_buffer_ != nullptr);
2458
2459 /* NOTE: Fallback buffer-backed texture always set to Texture2D. */
2460 texture_descriptor_.textureType = MTLTextureType2D;
2461 texture_descriptor_.depth = 1;
2462 texture_descriptor_.arrayLength = 1;
2463
2464 /* Write texture dimensions to metadata. This is required to remap 2D Array/3D sample
2465 * coordinates into 2D texture space. */
2466 tex_buffer_metadata_[0] = w_;
2467 tex_buffer_metadata_[1] = h_;
2468 tex_buffer_metadata_[2] = d_;
2469
2470 /* Texture allocation with buffer as backing storage. Bytes per row must satisfy alignment
2471 * rules for device. */
2472 uint32_t align_requirement = static_cast<uint32_t>(
2473 [ctx->device minimumLinearTextureAlignmentForPixelFormat:mtl_format]);
2474 size_t aligned_bytes_per_row = ceil_to_multiple_ul(bytes_per_row, align_requirement);
2475 texture_ = [backing_buffer_->get_metal_buffer()
2476 newTextureWithDescriptor:texture_descriptor_
2477 offset:0
2478 bytesPerRow:aligned_bytes_per_row];
2479 /* Aligned width. */
2480 tex_buffer_metadata_[3] = bytes_per_row / bytes_per_pixel;
2481
2482#ifndef NDEBUG
2483 texture_.label = [NSString
2484 stringWithFormat:@"AtomicBufferBackedTexture_%s", this->get_name()];
2485#endif
2486 }
2487 else {
2488
2489 /* Standard texture allocation. */
2490 texture_ = [ctx->device newTextureWithDescriptor:texture_descriptor_];
2491
2492#ifndef NDEBUG
2493 if (internal_gpu_image_usage_flags_ & GPU_TEXTURE_USAGE_MEMORYLESS) {
2494 texture_.label = [NSString stringWithFormat:@"MemorylessTexture_%s", this->get_name()];
2495 }
2496 else {
2497 texture_.label = [NSString stringWithFormat:@"Texture_%s", this->get_name()];
2498 }
2499#endif
2500 }
2501
2502 BLI_assert(texture_);
2503 is_baked_ = true;
2504 is_dirty_ = false;
2505 }
2506
2507 /* Re-apply previous contents. */
2508 if (copy_previous_contents) {
2509 /* TODO(Metal): May need to copy previous contents of texture into new texture. */
2510 [previous_texture release];
2511 }
2512}
2513
2514void gpu::MTLTexture::reset()
2515{
2516 MTL_LOG_INFO("Texture %s reset. Size %d, %d, %d", this->get_name(), w_, h_, d_);
2517 /* Delete associated METAL resources. */
2518 if (texture_ != nil) {
2519 [texture_ release];
2520 texture_ = nil;
2521 is_baked_ = false;
2522 is_dirty_ = true;
2523 }
2524
2525 /* Release backing Metal buffer, if used. */
2526 if (backing_buffer_ != nullptr) {
2527 backing_buffer_->free();
2528 backing_buffer_ = nullptr;
2529 }
2530
2531 /* Release backing storage buffer, if used. */
2532 if (storage_buffer_ != nullptr) {
2533 delete storage_buffer_;
2534 storage_buffer_ = nullptr;
2535 }
2536
2537 if (texture_no_srgb_ != nil) {
2538 [texture_no_srgb_ release];
2539 texture_no_srgb_ = nil;
2540 }
2541
2542 if (mip_swizzle_view_ != nil) {
2543 [mip_swizzle_view_ release];
2544 mip_swizzle_view_ = nil;
2545 }
2546
2547 /* Blit framebuffer. */
2548 if (blit_fb_) {
2549 GPU_framebuffer_free(blit_fb_);
2550 blit_fb_ = nullptr;
2551 }
2552
2553 /* Descriptor. */
2554 if (texture_descriptor_ != nullptr) {
2555 [texture_descriptor_ release];
2556 texture_descriptor_ = nullptr;
2557 }
2558
2559 /* Reset mipmap state. */
2560 has_generated_mips_ = false;
2561
2562 BLI_assert(texture_ == nil);
2563 BLI_assert(mip_swizzle_view_ == nil);
2564}
2565
2568/* -------------------------------------------------------------------- */
2572{
2574 backing_buffer_ != nullptr,
2575 "Resource must have been created as a buffer backed resource to support SSBO wrapping.");
2576 /* Ensure texture resource is up to date. */
2577 this->ensure_baked();
2578 if (storage_buffer_ == nil) {
2579 BLI_assert(texture_ != nullptr);
2580 id<MTLBuffer> backing_buffer = [texture_ buffer];
2581 BLI_assert(backing_buffer != nil);
2582 storage_buffer_ = new MTLStorageBuf(this, [backing_buffer length]);
2583 }
2584 return storage_buffer_;
2585}
2588/* -------------------------------------------------------------------- */
2592{
2593 return (format_ == GPU_SRGB8_A8);
2594}
2595
2596id<MTLTexture> MTLTexture::get_non_srgb_handle()
2597{
2598 id<MTLTexture> base_tex = get_metal_handle_base();
2599 BLI_assert(base_tex != nil);
2600 if (texture_no_srgb_ == nil) {
2601 texture_no_srgb_ = [base_tex newTextureViewWithPixelFormat:MTLPixelFormatRGBA8Unorm];
2602 }
2603 return texture_no_srgb_;
2604}
2605
2607/* -------------------------------------------------------------------- */
2612{
2613 /* Ensure buffer satisfies the alignment of 256 bytes for copying
2614 * data between buffers and textures. As specified in:
2615 * https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf */
2616 BLI_assert(size >= 256);
2617 buffer_ = nil;
2618}
2619
2621{
2622 if (buffer_) {
2623 [buffer_ release];
2624 buffer_ = nil;
2625 }
2626}
2627
2629{
2630 /* Duplicate the existing buffer and release original to ensure we do not directly modify data
2631 * in-flight on the GPU. */
2632 MTLContext *ctx = MTLContext::get();
2633 BLI_assert(ctx);
2634 MTLResourceOptions resource_options = ([ctx->device hasUnifiedMemory]) ?
2635 MTLResourceStorageModeShared :
2636 MTLResourceStorageModeManaged;
2637
2638 if (buffer_ != nil) {
2639 id<MTLBuffer> new_buffer = [ctx->device newBufferWithBytes:[buffer_ contents]
2641 options:resource_options];
2642 [buffer_ release];
2643 buffer_ = new_buffer;
2644 }
2645 else {
2646 buffer_ = [ctx->device newBufferWithLength:size_ options:resource_options];
2647 }
2648
2649 return [buffer_ contents];
2650}
2651
2653{
2654 if (buffer_ == nil) {
2655 return;
2656 }
2657
2658 /* Ensure changes are synchronized. */
2659 if (buffer_.resourceOptions & MTLResourceStorageModeManaged) {
2660 [buffer_ didModifyRange:NSMakeRange(0, size_)];
2661 }
2662}
2663
2665{
2666 if (buffer_ == nil) {
2667 return 0;
2668 }
2669
2670 return reinterpret_cast<int64_t>(buffer_);
2671}
2672
2674{
2675 return size_;
2676}
2677
2679{
2680 return buffer_;
2681}
2682
2685} // namespace blender::gpu
@ G_DEBUG_GPU
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:57
MINLINE int min_ii(int a, int b)
MINLINE uint divide_ceil_u(uint a, uint b)
MINLINE int max_ii(int a, int b)
MINLINE int max_iii(int a, int b, int c)
MINLINE uint64_t ceil_to_multiple_ul(uint64_t a, uint64_t b)
unsigned int uint
#define UNUSED_VARS_NDEBUG(...)
#define ELEM(...)
GHOST C-API function and type declarations.
#define GPU_batch_texture_bind(batch, name, tex)
Definition GPU_batch.hh:316
void GPU_batch_set_shader(blender::gpu::Batch *batch, GPUShader *shader)
void GPU_batch_draw(blender::gpu::Batch *batch)
blender::gpu::Batch * GPU_batch_preset_quad()
int GPU_max_texture_3d_size()
GPUFrameBuffer * GPU_framebuffer_create(const char *name)
GPUFrameBuffer * GPU_framebuffer_active_get()
void GPU_framebuffer_restore()
#define GPU_ATTACHMENT_NONE
void GPU_framebuffer_bind(GPUFrameBuffer *framebuffer)
#define GPU_framebuffer_ensure_config(_fb,...)
#define GPU_ATTACHMENT_TEXTURE_LAYER_MIP(_texture, _layer, _mip)
void GPU_framebuffer_free(GPUFrameBuffer *framebuffer)
@ GPU_ARCHITECTURE_TBDR
GPUArchitectureType GPU_platform_architecture()
void GPU_shader_uniform_1i(GPUShader *sh, const char *name, int value)
void GPU_shader_uniform_2f(GPUShader *sh, const char *name, float x, float y)
void GPU_face_culling(eGPUFaceCullTest culling)
Definition gpu_state.cc:47
eGPUFaceCullTest GPU_face_culling_get()
Definition gpu_state.cc:52
eGPUBlend
Definition GPU_state.hh:84
@ GPU_BLEND_NONE
Definition GPU_state.hh:85
void GPU_blend(eGPUBlend blend)
Definition gpu_state.cc:42
void GPU_scissor_test(bool enable)
Definition gpu_state.cc:183
eGPUBlend GPU_blend_get()
Definition gpu_state.cc:221
void GPU_finish()
Definition gpu_state.cc:299
void GPU_depth_mask(bool depth)
Definition gpu_state.cc:110
eGPUDepthTest GPU_depth_test_get()
Definition gpu_state.cc:239
void GPU_stencil_test(eGPUStencilTest test)
Definition gpu_state.cc:73
void GPU_stencil_write_mask_set(uint write_mask)
Definition gpu_state.cc:205
eGPUFaceCullTest
Definition GPU_state.hh:132
@ GPU_CULL_NONE
Definition GPU_state.hh:133
void GPU_stencil_reference_set(uint reference)
Definition gpu_state.cc:200
eGPUStencilTest GPU_stencil_test_get()
Definition gpu_state.cc:245
eGPUDepthTest
Definition GPU_state.hh:107
@ GPU_DEPTH_ALWAYS
Definition GPU_state.hh:109
uint GPU_stencil_mask_get()
Definition gpu_state.cc:233
eGPUStencilTest
Definition GPU_state.hh:117
@ GPU_STENCIL_ALWAYS
Definition GPU_state.hh:119
void GPU_depth_test(eGPUDepthTest test)
Definition gpu_state.cc:68
bool GPU_depth_mask_get()
Definition gpu_state.cc:276
eGPUDataFormat
@ GPU_DATA_UINT_24_8
@ GPU_DATA_INT
@ GPU_DATA_10_11_11_REV
@ GPU_DATA_UINT
@ GPU_DATA_2_10_10_10_REV
@ GPU_DATA_FLOAT
@ GPU_TEXTURE_USAGE_SHADER_WRITE
@ GPU_TEXTURE_USAGE_HOST_READ
@ GPU_TEXTURE_USAGE_MEMORYLESS
@ GPU_TEXTURE_USAGE_ATTACHMENT
@ GPU_TEXTURE_USAGE_ATOMIC
@ GPU_TEXTURE_USAGE_FORMAT_VIEW
eGPUTextureFormat
@ GPU_DEPTH32F_STENCIL8
@ GPU_SRGB8_A8
@ GPU_DEPTH24_STENCIL8
@ GPU_RGB10_A2
@ GPU_RGB10_A2UI
@ GPU_DEPTH_COMPONENT24
@ GPU_DEPTH_COMPONENT32F
@ GPU_DEPTH_COMPONENT16
eGPUTextureUsage GPU_texture_usage(const GPUTexture *texture)
const GPUVertFormat * GPU_vertbuf_get_format(const blender::gpu::VertBuf *verts)
struct GPUShader GPUShader
void init()
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
void reset()
clear internal cached data and reset random seed
SIMD_FORCE_INLINE btScalar length() const
Return the length of the vector.
Definition btVector3.h:257
static MTLCapabilities & get_capabilities()
gpu::MTLBuffer * allocate_with_data(uint64_t size, bool cpu_visible, const void *data=nullptr)
gpu::MTLBuffer * allocate(uint64_t size, bool cpu_visible)
Definition mtl_memory.mm:96
gpu::MTLBuffer * allocate_aligned(uint64_t size, uint alignment, bool cpu_visible)
id< MTLBuffer > get_metal_buffer() const
id< MTLComputeCommandEncoder > ensure_begin_compute_encoder()
id< MTLBlitCommandEncoder > ensure_begin_blit_encoder()
void bind_compute_bytes(const void *bytes, uint64_t length, uint index)
void bind_compute_texture(id< MTLTexture > tex, uint slot)
void bind_compute_buffer(id< MTLBuffer > buffer, uint64_t buffer_offset, uint index)
void bind_pso(id< MTLComputePipelineState > pso)
static MTLContext * get()
MTLContextGlobalShaderPipelineState pipeline_state
MTLCommandBufferManager main_command_buffer
static MTLBufferPool * get_global_memory_manager()
id< MTLBuffer > get_metal_buffer()
int64_t get_native_handle() override
uint gl_bindcode_get() const override
void * read(int mip, eGPUDataFormat type) override
void copy_to(Texture *dst) override
void update_sub(int mip, int offset[3], int extent[3], eGPUDataFormat type, const void *data) override
MTLTexture(const char *name)
void clear(eGPUDataFormat format, const void *data) override
MTLStorageBuf * get_storagebuf()
void mip_range_set(int min, int max) override
void generate_mipmap() override
bool init_internal() override
void swizzle_set(const char swizzle_mask[4]) override
virtual void texture_unbind(Texture *tex)=0
eGPUTextureUsage gpu_image_usage_flags_
bool init_2D(int w, int h, int layers, int mip_len, eGPUTextureFormat format)
CCL_NAMESPACE_BEGIN struct Options options
const char * label
DOF_TILES_FLATTEN_GROUP_SIZE coc_tx GPU_R11F_G11F_B10F
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
blender::gpu::Batch * quad
IndexRange range
BLI_INLINE float fb(float length, float L)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
format
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
MINLINE unsigned long long max_ulul(unsigned long long a, unsigned long long b)
ccl_device_inline float2 floor(const float2 a)
#define G(x, y, z)
#define MTL_LOG_INFO(info,...)
Definition mtl_debug.hh:51
#define MTL_LOG_WARNING(info,...)
Definition mtl_debug.hh:44
#define MTL_LOG_ERROR(info,...)
Definition mtl_debug.hh:36
std::string get_name(const VolumeGridData &grid)
size_t get_mtl_format_bytesize(MTLPixelFormat tex_format)
static Context * unwrap(GPUContext *ctx)
MTLPixelFormat gpu_texture_format_to_metal(eGPUTextureFormat tex_format)
std::string tex_data_format_to_msl_type_str(eGPUDataFormat type)
size_t to_block_size(eGPUTextureFormat data_type)
static GPUContext * wrap(Context *ctx)
constexpr bool validate_data_format(eGPUTextureFormat tex_format, eGPUDataFormat data_format)
std::string tex_data_format_to_msl_texture_template_type(eGPUDataFormat type)
MTLPixelFormat mtl_format_get_writeable_view_format(MTLPixelFormat format)
static MTLTextureSwizzle swizzle_to_mtl(const char swizzle)
size_t to_bytesize(GPUIndexBufType type)
int get_mtl_format_num_components(MTLPixelFormat tex_format)
eGPUDataFormat to_data_format(eGPUTextureFormat tex_format)
MTLTextureUsage mtl_usage_from_gpu(eGPUTextureUsage usage)
MTLTextureType to_metal_type(eGPUTextureType type)
int to_component_len(eGPUTextureFormat format)
eGPUTextureUsage gpu_usage_from_mtl(MTLTextureUsage mtl_usage)
#define min(a, b)
Definition sort.c:32
unsigned int uint32_t
Definition stdint.h:80
__int64 int64_t
Definition stdint.h:89
float max