@@ -50,6 +50,52 @@ static void dealloc(Object *self) {
5050
5151#pragma mark - Texture
5252
53+ /**
54+ * @brief Uploads @p size bytes of pixel data into mip level 0 of this texture.
55+ * @details The source data is laid out at @p pixelsPerRow pixels (texels) per row, so
56+ * callers can pass strided data (e.g. an `SDL_Surface` with row padding) without first
57+ * tightening it. The texture's dimensions and layer count come from its metadata, so
58+ * this must be called after `initWithDevice` has populated them.
59+ */
60+ static void uploadPixels (Texture * self , const void * pixels , Uint32 size , Uint32 pixelsPerRow ) {
61+
62+ SDL_GPUDevice * device = self -> device -> device ;
63+
64+ SDL_GPUTransferBuffer * tbuf = SDL_CreateGPUTransferBuffer (device , & (SDL_GPUTransferBufferCreateInfo ) {
65+ .usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD ,
66+ .size = size ,
67+ });
68+ GPU_Assert (tbuf , "SDL_CreateGPUTransferBuffer" );
69+
70+ void * mapped = SDL_MapGPUTransferBuffer (device , tbuf , false);
71+ GPU_Assert (mapped , "SDL_MapGPUTransferBuffer" );
72+
73+ memcpy (mapped , pixels , size );
74+ SDL_UnmapGPUTransferBuffer (device , tbuf );
75+
76+ CommandBuffer * commands = $ (self -> device , acquireCommandBuffer );
77+ CopyPass * copyPass = $ (commands , beginCopyPass );
78+
79+ $ (copyPass , uploadTexture ,
80+ & (SDL_GPUTextureTransferInfo ) {
81+ .transfer_buffer = tbuf ,
82+ .pixels_per_row = pixelsPerRow ,
83+ .rows_per_layer = (Uint32 ) self -> size .h ,
84+ },
85+ & (SDL_GPUTextureRegion ) {
86+ .texture = self -> texture ,
87+ .w = (Uint32 ) self -> size .w ,
88+ .h = (Uint32 ) self -> size .h ,
89+ .d = self -> layerCountOrDepth ,
90+ },
91+ false);
92+
93+ release (copyPass );
94+ $ (commands , submit );
95+ release (commands );
96+ SDL_ReleaseGPUTransferBuffer (device , tbuf );
97+ }
98+
5399/**
54100 * @fn Texture *Texture::initWithDevice(Texture *self, RenderDevice *device, const SDL_GPUTextureCreateInfo *info, const void *pixels)
55101 * @memberof Texture
@@ -76,42 +122,13 @@ static Texture *initWithDevice(Texture *self, RenderDevice *device, const SDL_GP
76122 self -> sampleCount = info -> sample_count ;
77123
78124 if (pixels ) {
79- const Uint32 bytesPerTexel = SDL_GPUTextureFormatTexelBlockSize (info -> format );
80- const Uint32 totalBytes = info -> width * info -> height * info -> layer_count_or_depth * bytesPerTexel ;
81-
82- SDL_GPUTransferBuffer * tbuf = SDL_CreateGPUTransferBuffer (device -> device , & (SDL_GPUTransferBufferCreateInfo ) {
83- .usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD ,
84- .size = totalBytes ,
85- });
86- GPU_Assert (tbuf , "SDL_CreateGPUTransferBuffer" );
87-
88- void * mapped = SDL_MapGPUTransferBuffer (device -> device , tbuf , false);
89- GPU_Assert (mapped , "SDL_MapGPUTransferBuffer" );
90-
91- memcpy (mapped , pixels , totalBytes );
92- SDL_UnmapGPUTransferBuffer (device -> device , tbuf );
93-
94- CommandBuffer * commands = $ (device , acquireCommandBuffer );
95- CopyPass * copyPass = $ (commands , beginCopyPass );
96-
97- $ (copyPass , uploadTexture ,
98- & (SDL_GPUTextureTransferInfo ) {
99- .transfer_buffer = tbuf ,
100- .pixels_per_row = info -> width ,
101- .rows_per_layer = info -> height ,
102- },
103- & (SDL_GPUTextureRegion ) {
104- .texture = self -> texture ,
105- .w = info -> width ,
106- .h = info -> height ,
107- .d = info -> layer_count_or_depth ,
108- },
109- false);
110-
111- release (copyPass );
112- $ (commands , submit );
113- release (commands );
114- SDL_ReleaseGPUTransferBuffer (device -> device , tbuf );
125+ // Tightly-packed source: SDL_CalculateGPUTextureFormatSize accounts for
126+ // block-compressed formats, so this is correct for BCn/ASTC/etc. as well as
127+ // plain texel formats (where a naive width*height*texelSize would also work).
128+ const Uint32 size = SDL_CalculateGPUTextureFormatSize (info -> format ,
129+ info -> width , info -> height , info -> layer_count_or_depth );
130+
131+ uploadPixels (self , pixels , size , info -> width );
115132 }
116133 }
117134
@@ -132,6 +149,9 @@ static Texture *initWithSurface(Texture *self, RenderDevice *device, SDL_Surface
132149 : SDL_ConvertSurface (surface , SDL_PIXELFORMAT_RGBA32 );
133150 GPU_Assert (rgba , "SDL_ConvertSurface" );
134151
152+ // Create the texture empty, then upload respecting the surface's row pitch — an
153+ // SDL_Surface's pitch is often larger than width * bytesPerPixel (alignment, or a
154+ // sub-surface of an atlas), so assuming tightly-packed rows would skew the image.
135155 self = $ (self , initWithDevice , device , & (SDL_GPUTextureCreateInfo ) {
136156 .type = SDL_GPU_TEXTURETYPE_2D ,
137157 .format = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM ,
@@ -141,7 +161,14 @@ static Texture *initWithSurface(Texture *self, RenderDevice *device, SDL_Surface
141161 .layer_count_or_depth = 1 ,
142162 .num_levels = 1 ,
143163 .sample_count = SDL_GPU_SAMPLECOUNT_1 ,
144- }, rgba -> pixels );
164+ }, NULL );
165+
166+ if (self ) {
167+ const Uint32 bytesPerPixel = SDL_BYTESPERPIXEL (rgba -> format );
168+ uploadPixels (self , rgba -> pixels ,
169+ (Uint32 ) rgba -> pitch * (Uint32 ) rgba -> h ,
170+ (Uint32 ) rgba -> pitch / bytesPerPixel );
171+ }
145172
146173 if (rgba != surface ) {
147174 SDL_DestroySurface (rgba );
0 commit comments