Metal alpha channel oddity












0















I'm trying to render to a MTLTexture within an MTKView and then pull the entire region back off the GPU and have an alpha pixel values of clear RGBA=0x00000000 really come off the GPU as just that -- in those regions where I haven't drawn anything.



Instead, all un-rendered areas come off as bright red, which is typical for how Metal renders areas on-screen that have otherwise not been rendered to by a vertex/fragment shader.



However, I've narrowed it down to this test case -- in which I don't actually render anything and send a clear texture on a round trip to the GPU and back:




  • I create my own texture ('clearTexture') with 0x00000000 pixel values

  • This texture ends up the GPU (MTLStorageModeManaged)

  • I then pull that texture off the GPU ('testTexture')

  • I grab that texture's pixel bytes

  • What do I notice? Pixel values of 0x0000ffff (pure red)


I'm baffled.



Here's the setup:



CGSize textureSize = CGSizeMake(1920,1080);

// create metalTextureDescriptor
MTLTextureDescriptor *metalTextureDescriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm
width:textureSize.width height:textureSize.height mipmapped:NO];
metalTextureDescriptor.storageMode = MTLStorageModeManaged;
metalTextureDescriptor.usage = MTLTextureUsageUnknown;

// create clear Texture
vector_uchar4 clearPixel = {0x00,0x00,0x00,0x00};
id<MTLTexture> clearTexture = [self fillTextureOfSize:textureSize withPixel:clearPixel];

// pull texture off GPU
id<MTLCommandBuffer> commandBuffer = [metalCommandQueue commandBuffer];
id<MTLTexture> testTexture = [self pullTextureOffGPU:clearTexture
withCommandBuffer:commandBuffer];

// a bunch of other rendering (I leave 'testTexture' untouched)

[commandBuffer commit];
[commandBuffer waitUntilCompleted];

// I then check out the pixels
[self debugTexture:testTexture];




At this point, when I break in the debug texture routine (below), the pixel bytes seen are 0x0000ffff and not the expected 0x00000000.



-(id<MTLTexture>)fillTextureOfSize:(CGSize)size withPixel:(vector_uchar4)pixel
{
id<MTLTexture> texture = [self.device newTextureWithDescriptor:metalTextureDescriptor];
NSUInteger pixelCount = size.width*size.height;
vector_uchar4 *buff = malloc(pixelCount*sizeof(vector_uchar4));
for (NSUInteger i=0; i<pixelCount; i++)
buff[i]=pixel;
[blackTexture replaceRegion:MTLRegionMake2D(0, 0, size.width, size.height)
mipmapLevel:0
withBytes:buff
bytesPerRow:size.width*sizeof(vector_uchar4)];
free(buff);
return texture;
}

-(id<MTLTexture>) pullTextureOffGPU:(id<MTLTexture>)inputTexture
withCommandBuffer:(id<MTLCommandBuffer>)commandBuffer
{
id<MTLTexture> retTexture = [self.device newTextureWithDescriptor:metalTextureDescriptor];
if (retTexture) {
id<MTLBlitCommandEncoder> blit = [commandBuffer blitCommandEncoder];
[blit copyFromTexture:inputTexture
sourceSlice:0
sourceLevel:0
sourceOrigin:MTLOriginMake(0, 0, 0)
sourceSize:MTLSizeMake(inputTexture.width,inputTexture.height,1)
toTexture:retTexture
destinationSlice:0
destinationLevel:0
destinationOrigin:MTLOriginMake(0, 0, 0)];
[blit synchronizeTexture:retTexture slice:0 level:0];
[blit endEncoding];
}
return retTexture;
}

-(void)debugTexture:(id<MTLTexture>)theTexture
{
NSInteger width = theTexture.width;
NSInteger height = theTexture.height;
void *buffer = malloc(width*height*4);
[theTexture getBytes:buffer
bytesPerRow:width*4
fromRegion:MTLRegionMake2D(0,0,width,height)
mipmapLevel:0];
free(buffer); // break here to examine buffer
}









share|improve this question























  • I don't know if this representative of your actual code, but, in the first snippet, you declare metalTextureDescriptor as a local variable. In the second snippet, you refer to it without it being passed in. I'm guessing there's an instance variable of that name. Are you sure you're using the descriptor you think you're using? Can you provide a real, complete program that demonstrates the issue?

    – Ken Thomases
    Jan 2 at 0:28











  • Ken... your challenge made me find my error. In my fillTextureOfSize method, I was always filling a 'blackTexture' instance and not the locally created one. Arg! It does now work. I will leave this here as an exemplar. Thanks!

    – zzyzy
    Jan 2 at 4:44











  • Glad you found it. You can post an answer of your own, so people know it's been answered.

    – Ken Thomases
    Jan 2 at 5:43
















0















I'm trying to render to a MTLTexture within an MTKView and then pull the entire region back off the GPU and have an alpha pixel values of clear RGBA=0x00000000 really come off the GPU as just that -- in those regions where I haven't drawn anything.



Instead, all un-rendered areas come off as bright red, which is typical for how Metal renders areas on-screen that have otherwise not been rendered to by a vertex/fragment shader.



However, I've narrowed it down to this test case -- in which I don't actually render anything and send a clear texture on a round trip to the GPU and back:




  • I create my own texture ('clearTexture') with 0x00000000 pixel values

  • This texture ends up the GPU (MTLStorageModeManaged)

  • I then pull that texture off the GPU ('testTexture')

  • I grab that texture's pixel bytes

  • What do I notice? Pixel values of 0x0000ffff (pure red)


I'm baffled.



Here's the setup:



CGSize textureSize = CGSizeMake(1920,1080);

// create metalTextureDescriptor
MTLTextureDescriptor *metalTextureDescriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm
width:textureSize.width height:textureSize.height mipmapped:NO];
metalTextureDescriptor.storageMode = MTLStorageModeManaged;
metalTextureDescriptor.usage = MTLTextureUsageUnknown;

// create clear Texture
vector_uchar4 clearPixel = {0x00,0x00,0x00,0x00};
id<MTLTexture> clearTexture = [self fillTextureOfSize:textureSize withPixel:clearPixel];

// pull texture off GPU
id<MTLCommandBuffer> commandBuffer = [metalCommandQueue commandBuffer];
id<MTLTexture> testTexture = [self pullTextureOffGPU:clearTexture
withCommandBuffer:commandBuffer];

// a bunch of other rendering (I leave 'testTexture' untouched)

[commandBuffer commit];
[commandBuffer waitUntilCompleted];

// I then check out the pixels
[self debugTexture:testTexture];




At this point, when I break in the debug texture routine (below), the pixel bytes seen are 0x0000ffff and not the expected 0x00000000.



-(id<MTLTexture>)fillTextureOfSize:(CGSize)size withPixel:(vector_uchar4)pixel
{
id<MTLTexture> texture = [self.device newTextureWithDescriptor:metalTextureDescriptor];
NSUInteger pixelCount = size.width*size.height;
vector_uchar4 *buff = malloc(pixelCount*sizeof(vector_uchar4));
for (NSUInteger i=0; i<pixelCount; i++)
buff[i]=pixel;
[blackTexture replaceRegion:MTLRegionMake2D(0, 0, size.width, size.height)
mipmapLevel:0
withBytes:buff
bytesPerRow:size.width*sizeof(vector_uchar4)];
free(buff);
return texture;
}

-(id<MTLTexture>) pullTextureOffGPU:(id<MTLTexture>)inputTexture
withCommandBuffer:(id<MTLCommandBuffer>)commandBuffer
{
id<MTLTexture> retTexture = [self.device newTextureWithDescriptor:metalTextureDescriptor];
if (retTexture) {
id<MTLBlitCommandEncoder> blit = [commandBuffer blitCommandEncoder];
[blit copyFromTexture:inputTexture
sourceSlice:0
sourceLevel:0
sourceOrigin:MTLOriginMake(0, 0, 0)
sourceSize:MTLSizeMake(inputTexture.width,inputTexture.height,1)
toTexture:retTexture
destinationSlice:0
destinationLevel:0
destinationOrigin:MTLOriginMake(0, 0, 0)];
[blit synchronizeTexture:retTexture slice:0 level:0];
[blit endEncoding];
}
return retTexture;
}

-(void)debugTexture:(id<MTLTexture>)theTexture
{
NSInteger width = theTexture.width;
NSInteger height = theTexture.height;
void *buffer = malloc(width*height*4);
[theTexture getBytes:buffer
bytesPerRow:width*4
fromRegion:MTLRegionMake2D(0,0,width,height)
mipmapLevel:0];
free(buffer); // break here to examine buffer
}









share|improve this question























  • I don't know if this representative of your actual code, but, in the first snippet, you declare metalTextureDescriptor as a local variable. In the second snippet, you refer to it without it being passed in. I'm guessing there's an instance variable of that name. Are you sure you're using the descriptor you think you're using? Can you provide a real, complete program that demonstrates the issue?

    – Ken Thomases
    Jan 2 at 0:28











  • Ken... your challenge made me find my error. In my fillTextureOfSize method, I was always filling a 'blackTexture' instance and not the locally created one. Arg! It does now work. I will leave this here as an exemplar. Thanks!

    – zzyzy
    Jan 2 at 4:44











  • Glad you found it. You can post an answer of your own, so people know it's been answered.

    – Ken Thomases
    Jan 2 at 5:43














0












0








0








I'm trying to render to a MTLTexture within an MTKView and then pull the entire region back off the GPU and have an alpha pixel values of clear RGBA=0x00000000 really come off the GPU as just that -- in those regions where I haven't drawn anything.



Instead, all un-rendered areas come off as bright red, which is typical for how Metal renders areas on-screen that have otherwise not been rendered to by a vertex/fragment shader.



However, I've narrowed it down to this test case -- in which I don't actually render anything and send a clear texture on a round trip to the GPU and back:




  • I create my own texture ('clearTexture') with 0x00000000 pixel values

  • This texture ends up the GPU (MTLStorageModeManaged)

  • I then pull that texture off the GPU ('testTexture')

  • I grab that texture's pixel bytes

  • What do I notice? Pixel values of 0x0000ffff (pure red)


I'm baffled.



Here's the setup:



CGSize textureSize = CGSizeMake(1920,1080);

// create metalTextureDescriptor
MTLTextureDescriptor *metalTextureDescriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm
width:textureSize.width height:textureSize.height mipmapped:NO];
metalTextureDescriptor.storageMode = MTLStorageModeManaged;
metalTextureDescriptor.usage = MTLTextureUsageUnknown;

// create clear Texture
vector_uchar4 clearPixel = {0x00,0x00,0x00,0x00};
id<MTLTexture> clearTexture = [self fillTextureOfSize:textureSize withPixel:clearPixel];

// pull texture off GPU
id<MTLCommandBuffer> commandBuffer = [metalCommandQueue commandBuffer];
id<MTLTexture> testTexture = [self pullTextureOffGPU:clearTexture
withCommandBuffer:commandBuffer];

// a bunch of other rendering (I leave 'testTexture' untouched)

[commandBuffer commit];
[commandBuffer waitUntilCompleted];

// I then check out the pixels
[self debugTexture:testTexture];




At this point, when I break in the debug texture routine (below), the pixel bytes seen are 0x0000ffff and not the expected 0x00000000.



-(id<MTLTexture>)fillTextureOfSize:(CGSize)size withPixel:(vector_uchar4)pixel
{
id<MTLTexture> texture = [self.device newTextureWithDescriptor:metalTextureDescriptor];
NSUInteger pixelCount = size.width*size.height;
vector_uchar4 *buff = malloc(pixelCount*sizeof(vector_uchar4));
for (NSUInteger i=0; i<pixelCount; i++)
buff[i]=pixel;
[blackTexture replaceRegion:MTLRegionMake2D(0, 0, size.width, size.height)
mipmapLevel:0
withBytes:buff
bytesPerRow:size.width*sizeof(vector_uchar4)];
free(buff);
return texture;
}

-(id<MTLTexture>) pullTextureOffGPU:(id<MTLTexture>)inputTexture
withCommandBuffer:(id<MTLCommandBuffer>)commandBuffer
{
id<MTLTexture> retTexture = [self.device newTextureWithDescriptor:metalTextureDescriptor];
if (retTexture) {
id<MTLBlitCommandEncoder> blit = [commandBuffer blitCommandEncoder];
[blit copyFromTexture:inputTexture
sourceSlice:0
sourceLevel:0
sourceOrigin:MTLOriginMake(0, 0, 0)
sourceSize:MTLSizeMake(inputTexture.width,inputTexture.height,1)
toTexture:retTexture
destinationSlice:0
destinationLevel:0
destinationOrigin:MTLOriginMake(0, 0, 0)];
[blit synchronizeTexture:retTexture slice:0 level:0];
[blit endEncoding];
}
return retTexture;
}

-(void)debugTexture:(id<MTLTexture>)theTexture
{
NSInteger width = theTexture.width;
NSInteger height = theTexture.height;
void *buffer = malloc(width*height*4);
[theTexture getBytes:buffer
bytesPerRow:width*4
fromRegion:MTLRegionMake2D(0,0,width,height)
mipmapLevel:0];
free(buffer); // break here to examine buffer
}









share|improve this question














I'm trying to render to a MTLTexture within an MTKView and then pull the entire region back off the GPU and have an alpha pixel values of clear RGBA=0x00000000 really come off the GPU as just that -- in those regions where I haven't drawn anything.



Instead, all un-rendered areas come off as bright red, which is typical for how Metal renders areas on-screen that have otherwise not been rendered to by a vertex/fragment shader.



However, I've narrowed it down to this test case -- in which I don't actually render anything and send a clear texture on a round trip to the GPU and back:




  • I create my own texture ('clearTexture') with 0x00000000 pixel values

  • This texture ends up the GPU (MTLStorageModeManaged)

  • I then pull that texture off the GPU ('testTexture')

  • I grab that texture's pixel bytes

  • What do I notice? Pixel values of 0x0000ffff (pure red)


I'm baffled.



Here's the setup:



CGSize textureSize = CGSizeMake(1920,1080);

// create metalTextureDescriptor
MTLTextureDescriptor *metalTextureDescriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm
width:textureSize.width height:textureSize.height mipmapped:NO];
metalTextureDescriptor.storageMode = MTLStorageModeManaged;
metalTextureDescriptor.usage = MTLTextureUsageUnknown;

// create clear Texture
vector_uchar4 clearPixel = {0x00,0x00,0x00,0x00};
id<MTLTexture> clearTexture = [self fillTextureOfSize:textureSize withPixel:clearPixel];

// pull texture off GPU
id<MTLCommandBuffer> commandBuffer = [metalCommandQueue commandBuffer];
id<MTLTexture> testTexture = [self pullTextureOffGPU:clearTexture
withCommandBuffer:commandBuffer];

// a bunch of other rendering (I leave 'testTexture' untouched)

[commandBuffer commit];
[commandBuffer waitUntilCompleted];

// I then check out the pixels
[self debugTexture:testTexture];




At this point, when I break in the debug texture routine (below), the pixel bytes seen are 0x0000ffff and not the expected 0x00000000.



-(id<MTLTexture>)fillTextureOfSize:(CGSize)size withPixel:(vector_uchar4)pixel
{
id<MTLTexture> texture = [self.device newTextureWithDescriptor:metalTextureDescriptor];
NSUInteger pixelCount = size.width*size.height;
vector_uchar4 *buff = malloc(pixelCount*sizeof(vector_uchar4));
for (NSUInteger i=0; i<pixelCount; i++)
buff[i]=pixel;
[blackTexture replaceRegion:MTLRegionMake2D(0, 0, size.width, size.height)
mipmapLevel:0
withBytes:buff
bytesPerRow:size.width*sizeof(vector_uchar4)];
free(buff);
return texture;
}

-(id<MTLTexture>) pullTextureOffGPU:(id<MTLTexture>)inputTexture
withCommandBuffer:(id<MTLCommandBuffer>)commandBuffer
{
id<MTLTexture> retTexture = [self.device newTextureWithDescriptor:metalTextureDescriptor];
if (retTexture) {
id<MTLBlitCommandEncoder> blit = [commandBuffer blitCommandEncoder];
[blit copyFromTexture:inputTexture
sourceSlice:0
sourceLevel:0
sourceOrigin:MTLOriginMake(0, 0, 0)
sourceSize:MTLSizeMake(inputTexture.width,inputTexture.height,1)
toTexture:retTexture
destinationSlice:0
destinationLevel:0
destinationOrigin:MTLOriginMake(0, 0, 0)];
[blit synchronizeTexture:retTexture slice:0 level:0];
[blit endEncoding];
}
return retTexture;
}

-(void)debugTexture:(id<MTLTexture>)theTexture
{
NSInteger width = theTexture.width;
NSInteger height = theTexture.height;
void *buffer = malloc(width*height*4);
[theTexture getBytes:buffer
bytesPerRow:width*4
fromRegion:MTLRegionMake2D(0,0,width,height)
mipmapLevel:0];
free(buffer); // break here to examine buffer
}






metal metalkit






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Jan 1 at 22:06









zzyzyzzyzy

543314




543314













  • I don't know if this representative of your actual code, but, in the first snippet, you declare metalTextureDescriptor as a local variable. In the second snippet, you refer to it without it being passed in. I'm guessing there's an instance variable of that name. Are you sure you're using the descriptor you think you're using? Can you provide a real, complete program that demonstrates the issue?

    – Ken Thomases
    Jan 2 at 0:28











  • Ken... your challenge made me find my error. In my fillTextureOfSize method, I was always filling a 'blackTexture' instance and not the locally created one. Arg! It does now work. I will leave this here as an exemplar. Thanks!

    – zzyzy
    Jan 2 at 4:44











  • Glad you found it. You can post an answer of your own, so people know it's been answered.

    – Ken Thomases
    Jan 2 at 5:43



















  • I don't know if this representative of your actual code, but, in the first snippet, you declare metalTextureDescriptor as a local variable. In the second snippet, you refer to it without it being passed in. I'm guessing there's an instance variable of that name. Are you sure you're using the descriptor you think you're using? Can you provide a real, complete program that demonstrates the issue?

    – Ken Thomases
    Jan 2 at 0:28











  • Ken... your challenge made me find my error. In my fillTextureOfSize method, I was always filling a 'blackTexture' instance and not the locally created one. Arg! It does now work. I will leave this here as an exemplar. Thanks!

    – zzyzy
    Jan 2 at 4:44











  • Glad you found it. You can post an answer of your own, so people know it's been answered.

    – Ken Thomases
    Jan 2 at 5:43

















I don't know if this representative of your actual code, but, in the first snippet, you declare metalTextureDescriptor as a local variable. In the second snippet, you refer to it without it being passed in. I'm guessing there's an instance variable of that name. Are you sure you're using the descriptor you think you're using? Can you provide a real, complete program that demonstrates the issue?

– Ken Thomases
Jan 2 at 0:28





I don't know if this representative of your actual code, but, in the first snippet, you declare metalTextureDescriptor as a local variable. In the second snippet, you refer to it without it being passed in. I'm guessing there's an instance variable of that name. Are you sure you're using the descriptor you think you're using? Can you provide a real, complete program that demonstrates the issue?

– Ken Thomases
Jan 2 at 0:28













Ken... your challenge made me find my error. In my fillTextureOfSize method, I was always filling a 'blackTexture' instance and not the locally created one. Arg! It does now work. I will leave this here as an exemplar. Thanks!

– zzyzy
Jan 2 at 4:44





Ken... your challenge made me find my error. In my fillTextureOfSize method, I was always filling a 'blackTexture' instance and not the locally created one. Arg! It does now work. I will leave this here as an exemplar. Thanks!

– zzyzy
Jan 2 at 4:44













Glad you found it. You can post an answer of your own, so people know it's been answered.

– Ken Thomases
Jan 2 at 5:43





Glad you found it. You can post an answer of your own, so people know it's been answered.

– Ken Thomases
Jan 2 at 5:43












0






active

oldest

votes











Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53999314%2fmetal-alpha-channel-oddity%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























0






active

oldest

votes








0






active

oldest

votes









active

oldest

votes






active

oldest

votes
















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53999314%2fmetal-alpha-channel-oddity%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

MongoDB - Not Authorized To Execute Command

Npm cannot find a required file even through it is in the searched directory

How to fix TextFormField cause rebuild widget in Flutter