Skip to content
Permalink
Browse files

8233226: Implement XOR Mode rendering option

  • Loading branch information
aghaisas committed Aug 3, 2020
1 parent bd42485 commit 9cf06e4092c3c44b301acdf9d9be9171bcd87876
@@ -59,6 +59,20 @@ struct GradShaderInOut {
float2 texCoords;
};


struct ColShaderInOut_XOR {
float4 position [[position]];
float2 orig_pos;
half4 color;
};

struct TxtShaderInOut_XOR {
float4 position [[position]];
float2 orig_pos;
float2 texCoords;
float2 tpCoords;
};

vertex ColShaderInOut vert_col(VertexInput in [[stage_in]],
constant FrameUniforms& uniforms [[buffer(FrameUniformBuffer)]],
constant TransformMatrix& transform [[buffer(MatrixBuffer)]]) {
@@ -362,27 +376,7 @@ fragment half4 frag_tp(
//TODO : implement alpha component value if source is transparent
}

fragment half4 frag_tp_xorMode(
TxtShaderInOut vert [[stage_in]],
texture2d<float, access::sample> renderTexture [[texture(0)]],
constant int& xorColor[[buffer(0)]])
{
constexpr sampler textureSampler (address::repeat,
mag_filter::nearest,
min_filter::nearest);

float4 pixelColor = renderTexture.sample(textureSampler, vert.texCoords);

pixelColor.r = float( (unsigned char)(pixelColor.r * 255.0) ^ ((xorColor >> 16) & 0xFF) ) / 255.0f;
pixelColor.g = float( (unsigned char)(pixelColor.g * 255.0) ^ ((xorColor >> 8) & 0xFF)) / 255.0f;
pixelColor.b = float( (unsigned char)(pixelColor.b * 255.0) ^ (xorColor & 0xFF)) / 255.0f;
pixelColor.a = 1.0;

return half4(pixelColor.r, pixelColor.g, pixelColor.b, 1.0);

// This implementation defaults alpha to 1.0 as if source is opaque
//TODO : implement alpha component value if source is transparent
}

/* The variables involved in the equation can be expressed as follows:
*
@@ -440,4 +434,143 @@ kernel void stencil2tex(const device uchar *imageBuffer [[buffer(0)]],
{
uchar p = imageBuffer[gid];
outputBuffer[gid] = uchar4(p, p, p, p);
}
}

// ----------------------------------------------------------------------------
// Shaders for rendering in XOR Mode
// ----------------------------------------------------------------------------
vertex ColShaderInOut_XOR vert_col_xorMode(VertexInput in [[stage_in]],
constant FrameUniforms& uniforms [[buffer(FrameUniformBuffer)]],
constant TransformMatrix& transform [[buffer(MatrixBuffer)]])
{
ColShaderInOut_XOR out;
float4 pos4 = float4(in.position, 0.0, 1.0);
out.position = transform.transformMatrix*pos4;
out.orig_pos = in.position;
out.color = half4(uniforms.color.r, uniforms.color.g, uniforms.color.b, uniforms.color.a);
return out;
}

fragment half4 frag_col_xorMode(ColShaderInOut_XOR in [[stage_in]],
texture2d<float, access::read> renderTexture [[texture(0)]])
{
uint2 texCoord = {(unsigned int)(in.orig_pos.x), (unsigned int)(in.orig_pos.y)};

float4 pixelColor = renderTexture.read(texCoord);
half4 color = in.color;

half4 c;
c.r = float( (unsigned char)(pixelColor.r * 255.0) ^ (unsigned char)(color.r * 255.0)) / 255.0f;
c.g = float( (unsigned char)(pixelColor.g * 255.0) ^ (unsigned char)(color.g * 255.0)) / 255.0f;
c.b = float( (unsigned char)(pixelColor.b * 255.0) ^ (unsigned char)(color.b * 255.0)) / 255.0f;
c.a = 1.0;

return c;
}


vertex TxtShaderInOut_XOR vert_txt_xorMode(
TxtVertexInput in [[stage_in]],
constant TransformMatrix& transform [[buffer(MatrixBuffer)]])
{
TxtShaderInOut_XOR out;
float4 pos4 = float4(in.position, 0.0, 1.0);
out.position = transform.transformMatrix*pos4;
out.orig_pos = in.position;
out.texCoords = in.texCoords;
return out;
}

fragment half4 frag_txt_xorMode(
TxtShaderInOut_XOR vert [[stage_in]],
texture2d<float, access::sample> renderTexture [[texture(0)]],
texture2d<float, access::read> backgroundTexture [[texture(1)]],
constant TxtFrameUniforms& uniforms [[buffer(1)]],
sampler textureSampler [[sampler(0)]])
{
uint2 texCoord = {(unsigned int)(vert.orig_pos.x), (unsigned int)(vert.orig_pos.y)};
float4 bgColor = backgroundTexture.read(texCoord);

float4 pixelColor = renderTexture.sample(textureSampler, vert.texCoords);
float srcA = uniforms.isSrcOpaque ? 1 : pixelColor.a;

float4 c;
if (uniforms.mode) {
c = mix(pixelColor, uniforms.color, srcA);
} else {
c = float4(pixelColor.r,
pixelColor.g,
pixelColor.b, srcA*uniforms.extraAlpha);
}

half4 ret;
ret.r = half( (unsigned char)(c.r * 255.0) ^ (unsigned char)(bgColor.r * 255.0)) / 255.0f;
ret.g = half( (unsigned char)(c.g * 255.0) ^ (unsigned char)(bgColor.g * 255.0)) / 255.0f;
ret.b = half( (unsigned char)(c.b * 255.0) ^ (unsigned char)(bgColor.b * 255.0)) / 255.0f;
ret.a = c.a;

return ret;
}


/*
// --------------------------------------------------------------------------------------
Currently, gradient paint and texture paint XOR mode rendering has been implemented
through tile based rendering (similar to OGL) that uses MTLBlitLoops_SurfaceToSwBlit method for
getting framebuffer tiles and render using a different render pipe (not MTLRenderer)
In metal, we can avoid tile based rendering and use below shaders.
NOTE: These two shaders are incomplete and need some tweak.
// --------------------------------------------------------------------------------------
fragment half4 frag_grad_xorMode(GradShaderInOut_XOR in [[stage_in]],
texture2d<float, access::read> renderTexture [[texture(0)]],
constant GradFrameUniforms& uniforms [[buffer(0)]]) {
uint2 texCoord = {(unsigned int)(in.orig_pos.x), (unsigned int)(in.orig_pos.y)};
float4 pixelColor = renderTexture.read(texCoord);
float3 v = float3(in.position.x, in.position.y, 1);
float a = (dot(v,uniforms.params)-0.25)*2.0;
float4 c = mix(uniforms.color1, uniforms.color2, a);
half4 ret;
ret.r = float( (unsigned char)(pixelColor.r * 255.0) ^ (unsigned char)(c.r * 255.0)) / 255.0f;
ret.g = float( (unsigned char)(pixelColor.g * 255.0) ^ (unsigned char)(c.g * 255.0)) / 255.0f;
ret.b = float( (unsigned char)(pixelColor.b * 255.0) ^ (unsigned char)(c.b * 255.0)) / 255.0f;
return half4(ret);
}
fragment half4 frag_tp_xorMode(
TxtShaderInOut vert [[stage_in]],
texture2d<float, access::sample> renderTexture [[texture(0)]],
texture2d<float, access::read> backgroundTexture [[texture(1)]],
constant int& xorColor[[buffer(0)]])
{
uint2 texCoord = {(unsigned int)(vert.orig_pos.x), (unsigned int)(vert.orig_pos.y)};
float4 bgColor = backgroundTexture.read(texCoord);
constexpr sampler textureSampler (address::repeat,
mag_filter::nearest,
min_filter::nearest);
float4 pixelColor = renderTexture.sample(textureSampler, vert.texCoords);
pixelColor.r = float( (unsigned char)(pixelColor.r * 255.0) ^ ((xorColor >> 16) & 0xFF) ) / 255.0f;
pixelColor.g = float( (unsigned char)(pixelColor.g * 255.0) ^ ((xorColor >> 8) & 0xFF)) / 255.0f;
pixelColor.b = float( (unsigned char)(pixelColor.b * 255.0) ^ (xorColor & 0xFF)) / 255.0f;
pixelColor.a = 1.0;
half4 ret;
ret.r = half( (unsigned char)(pixelColor.r * 255.0) ^ (unsigned char)(bgColor.r * 255.0)) / 255.0f;
ret.g = half( (unsigned char)(pixelColor.g * 255.0) ^ (unsigned char)(bgColor.g * 255.0)) / 255.0f;
ret.b = half( (unsigned char)(pixelColor.b * 255.0) ^ (unsigned char)(bgColor.b * 255.0)) / 255.0f;
ret.a = 1.0;
return ret;
// This implementation defaults alpha to 1.0 as if source is opaque
//TODO : implement alpha component value if source is transparent
}
*/
@@ -163,6 +163,7 @@
*/
- (void)setXorComposite:(jint)xorPixel;
- (jboolean)isBlendingDisabled:(jboolean) isSrcOpaque;
- (jboolean)useXORComposite;

/**
* Resets the OpenGL transform state back to the identity matrix.
@@ -275,6 +275,9 @@ - (jboolean)isBlendingDisabled:(jboolean) isSrcOpaque {
return [_composite isBlendingDisabled:isSrcOpaque];
}

- (jboolean) useXORComposite {
return ([_composite getCompositeState] == sun_java2d_SunGraphics2D_COMP_XOR);
}

- (void)resetTransform {
J2dTraceLn(J2D_TRACE_INFO, "MTLContext_ResetTransform");
@@ -35,6 +35,7 @@
#include "sun_java2d_pipe_BufferedPaints.h"
#import "MTLComposite.h"
#import "MTLBufImgOps.h"
#include "MTLRenderQueue.h"

#define RGBA_TO_V4(c) \
{ \
@@ -458,6 +459,7 @@ - (void)setPipelineState:(id<MTLRenderCommandEncoder>)encoder

[encoder setVertexBytes:&_anchor length:sizeof(_anchor) atIndex:FrameUniformBuffer];
[encoder setFragmentTexture:_paintTexture atIndex:0];

}
}

@@ -483,46 +485,69 @@ - (void)setXorModePipelineState:(id<MTLRenderCommandEncoder>)encoder

jint xorColor = (jint) [mtlc.composite getXorColor];

NSString * vertShader = @"vert_txt";
NSString * fragShader = @"frag_txt";
NSString * vertShader = @"vert_txt_xorMode";
NSString * fragShader = @"frag_txt_xorMode";
MTLRenderPipelineDescriptor * rpDesc = templateTexturePipelineDesc;

if (renderOptions->isTexture) {
const int col = _paintState == sun_java2d_SunGraphics2D_PAINT_ALPHACOLOR ? _color ^ xorColor : 0 ^ xorColor;
setTxtUniforms(encoder, col, _paintState == sun_java2d_SunGraphics2D_PAINT_ALPHACOLOR ? 1 : 0, renderOptions->interpolation, NO, [mtlc.composite getExtraAlpha], &renderOptions->srcFlags, &renderOptions->dstFlags);
setTxtUniforms(encoder, col, _paintState == sun_java2d_SunGraphics2D_PAINT_ALPHACOLOR ? 1 : 0,
renderOptions->interpolation, NO, [mtlc.composite getExtraAlpha],
&renderOptions->srcFlags, &renderOptions->dstFlags);
[encoder setFragmentBytes:&xorColor length:sizeof(xorColor) atIndex: 0];

BMTLSDOps *dstOps = MTLRenderQueue_GetCurrentDestination();
[encoder setFragmentTexture:dstOps->pTexture atIndex:1];

J2dTraceLn(J2D_TRACE_INFO, "MTLPaints - setXorModePipelineState -- TEXTURE DRAW");
} else {
if (_paintState == sun_java2d_SunGraphics2D_PAINT_ALPHACOLOR) {
vertShader = @"vert_col";
fragShader = @"frag_col";
vertShader = @"vert_col_xorMode";
fragShader = @"frag_col_xorMode";
rpDesc = templateRenderPipelineDesc;

// Calculate _color ^ xorColor for RGB components
// This color gets XORed with destination framebuffer pixel color
struct FrameUniforms uf = {RGBA_TO_V4(_color ^ xorColor)};
[encoder setVertexBytes:&uf length:sizeof(uf) atIndex:FrameUniformBuffer];

BMTLSDOps *dstOps = MTLRenderQueue_GetCurrentDestination();
[encoder setFragmentTexture:dstOps->pTexture atIndex:0];

J2dTraceLn(J2D_TRACE_INFO ,"MTLPaints - setXorModePipelineState -- PAINT_ALPHACOLOR");
} else if (_paintState == sun_java2d_SunGraphics2D_PAINT_GRADIENT) {
vertShader = @"vert_grad";
fragShader = @"frag_grad";
// This block is not reached in current implementation.
// Gradient paint XOR mode rendering uses a tile based rendering using a SW pipe (similar to OGL)
vertShader = @"vert_grad_xorMode";
fragShader = @"frag_grad_xorMode";
rpDesc = templateRenderPipelineDesc;

struct GradFrameUniforms uf = {
struct GradFrameUniforms uf = {
{_p0, _p1, _p3},
RGBA_TO_V4(_pixel1 ^ xorColor),
RGBA_TO_V4(_pixel2 ^ xorColor)};
[encoder setFragmentBytes: &uf length:sizeof(uf) atIndex:0];
} else if (_paintState == sun_java2d_SunGraphics2D_PAINT_TEXTURE) {
vertShader = @"vert_tp";

[encoder setFragmentBytes: &uf length:sizeof(uf) atIndex:0];
BMTLSDOps *dstOps = MTLRenderQueue_GetCurrentDestination();
[encoder setFragmentTexture:dstOps->pTexture atIndex:0];

J2dTraceLn(J2D_TRACE_INFO, "MTLPaints - setXorModePipelineState -- PAINT_GRADIENT");
} else if (_paintState == sun_java2d_SunGraphics2D_PAINT_TEXTURE) {
// This block is not reached in current implementation.
// Texture paint XOR mode rendering uses a tile based rendering using a SW pipe (similar to OGL)
vertShader = @"vert_tp_xorMode";
fragShader = @"frag_tp_xorMode";
rpDesc = templateRenderPipelineDesc;

[encoder setVertexBytes:&_anchor length:sizeof(_anchor) atIndex:FrameUniformBuffer];
[encoder setFragmentTexture:_paintTexture atIndex: 0];
BMTLSDOps *dstOps = MTLRenderQueue_GetCurrentDestination();
[encoder setFragmentTexture:dstOps->pTexture atIndex:1];
[encoder setFragmentBytes:&xorColor length:sizeof(xorColor) atIndex: 0];

[encoder setVertexBytes:&_anchor length:sizeof(_anchor) atIndex:FrameUniformBuffer];
[encoder setFragmentTexture:_paintTexture atIndex: 0];
[encoder setFragmentBytes:&xorColor length:sizeof(xorColor) atIndex: 0];
}
J2dTraceLn(J2D_TRACE_INFO, "MTLPaints - setXorModePipelineState -- PAINT_TEXTURE");
}
}

id <MTLRenderPipelineState> pipelineState = [pipelineStateStorage getPipelineState:rpDesc
vertexShaderId:vertShader
@@ -130,12 +130,20 @@ - (NSPointerArray * ) getSubStates:(NSString *)vertexShaderId fragmentShader:(NS
pipelineDesc.fragmentFunction = fragmentShader;

if (useXorComposite) {
/* The below configuration is the best performant implementation of XOR mode rendering.
It was found that it works ONLY for basic Colors and not for all RGB combinations.
Hence, a slow performant XOR mode rendering has been implemented by
disabling blending & committing after each draw call.
In XOR mode rendering, subsequent draw calls are rendered
by shader using already rendered framebuffer pixel value XORed
with current draw color and XOR color.
pipelineDesc.colorAttachments[0].blendingEnabled = YES;

pipelineDesc.colorAttachments[0].rgbBlendOperation = MTLBlendOperationAdd;
pipelineDesc.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorOneMinusDestinationColor;
pipelineDesc.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceColor;
*/

pipelineDesc.colorAttachments[0].blendingEnabled = NO;
} else if (useComposite ||
(composite != nil &&
FLT_LT([composite getExtraAlpha], 1.0f)))
@@ -89,6 +89,7 @@ enum {

MTLContext *MTLRenderQueue_GetCurrentContext();
BMTLSDOps *MTLRenderQueue_GetCurrentDestination();
void commitEncodedCommands();

extern jint mtlPreviousOp;

0 comments on commit 9cf06e4

Please sign in to comment.
You can’t perform that action at this time.