2017-08-01 18:20:43 +00:00
// Copyright 2017 The NXT Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
# include <array>
# include <cmath>
# include "tests/NXTTest.h"
# include "common/Assert.h"
# include "common/Constants.h"
# include "utils/NXTHelpers.h"
constexpr static unsigned int kRTSize = 64 ;
class BlendStateTest : public NXTTest {
protected :
void SetUp ( ) override {
NXTTest : : SetUp ( ) ;
vsModule = utils : : CreateShaderModule ( device , nxt : : ShaderStage : : Vertex , R " (
# version 450
void main ( ) {
const vec2 pos [ 3 ] = vec2 [ 3 ] ( vec2 ( - 1.f , - 1.f ) , vec2 ( 3.f , - 1.f ) , vec2 ( - 1.f , 3.f ) ) ;
gl_Position = vec4 ( pos [ gl_VertexIndex ] , 0.f , 1.f ) ;
}
) " );
bindGroupLayout = device . CreateBindGroupLayoutBuilder ( )
. SetBindingsType ( nxt : : ShaderStageBit : : Fragment , nxt : : BindingType : : UniformBuffer , 0 , 1 )
. GetResult ( ) ;
pipelineLayout = device . CreatePipelineLayoutBuilder ( )
. SetBindGroupLayout ( 0 , bindGroupLayout )
. GetResult ( ) ;
2018-02-09 21:51:02 +00:00
fb = utils : : CreateBasicFramebuffer ( device , kRTSize , kRTSize ) ;
2017-08-01 18:20:43 +00:00
}
struct TriangleSpec {
RGBA8 color ;
std : : array < float , 4 > blendFactor = { } ;
} ;
// Set up basePipeline and testPipeline. testPipeline has the given blend state on the first attachment. basePipeline has no blending
void SetupSingleSourcePipelines ( const nxt : : BlendState & blendState ) {
nxt : : ShaderModule fsModule = utils : : CreateShaderModule ( device , nxt : : ShaderStage : : Fragment , R " (
# version 450
layout ( set = 0 , binding = 0 ) uniform myBlock {
vec4 color ;
} myUbo ;
layout ( location = 0 ) out vec4 fragColor ;
void main ( ) {
fragColor = myUbo . color ;
}
) " );
basePipeline = device . CreateRenderPipelineBuilder ( )
2018-02-09 21:51:02 +00:00
. SetSubpass ( fb . renderPass , 0 )
2017-08-01 18:20:43 +00:00
. SetLayout ( pipelineLayout )
. SetStage ( nxt : : ShaderStage : : Vertex , vsModule , " main " )
. SetStage ( nxt : : ShaderStage : : Fragment , fsModule , " main " )
. GetResult ( ) ;
testPipeline = device . CreateRenderPipelineBuilder ( )
2018-02-09 21:51:02 +00:00
. SetSubpass ( fb . renderPass , 0 )
2017-08-01 18:20:43 +00:00
. SetLayout ( pipelineLayout )
. SetStage ( nxt : : ShaderStage : : Vertex , vsModule , " main " )
. SetStage ( nxt : : ShaderStage : : Fragment , fsModule , " main " )
. SetColorAttachmentBlendState ( 0 , blendState )
. GetResult ( ) ;
}
// Create a bind group to set the colors as a uniform buffer
template < size_t N >
nxt : : BindGroup MakeBindGroupForColors ( std : : array < RGBA8 , N > colors ) {
std : : array < float , 4 * N > data ;
for ( unsigned int i = 0 ; i < N ; + + i ) {
data [ 4 * i + 0 ] = static_cast < float > ( colors [ i ] . r ) / 255.f ;
data [ 4 * i + 1 ] = static_cast < float > ( colors [ i ] . g ) / 255.f ;
data [ 4 * i + 2 ] = static_cast < float > ( colors [ i ] . b ) / 255.f ;
data [ 4 * i + 3 ] = static_cast < float > ( colors [ i ] . a ) / 255.f ;
}
uint32_t bufferSize = static_cast < uint32_t > ( 4 * N * sizeof ( float ) ) ;
nxt : : Buffer buffer = utils : : CreateFrozenBufferFromData ( device , & data , bufferSize , nxt : : BufferUsageBit : : Uniform ) ;
nxt : : BufferView view = buffer . CreateBufferViewBuilder ( )
. SetExtent ( 0 , bufferSize )
. GetResult ( ) ;
return device . CreateBindGroupBuilder ( )
. SetLayout ( bindGroupLayout )
. SetUsage ( nxt : : BindGroupUsage : : Frozen )
. SetBufferViews ( 0 , 1 , & view )
. GetResult ( ) ;
}
// Test that after drawing a triangle with the base color, and then the given triangle spec, the color is as expected
void DoSingleSourceTest ( RGBA8 base , const TriangleSpec & triangle , const RGBA8 & expected ) {
2018-02-09 21:51:02 +00:00
fb . color . TransitionUsage ( nxt : : TextureUsageBit : : OutputAttachment ) ;
2017-08-01 18:20:43 +00:00
nxt : : CommandBuffer commands = device . CreateCommandBufferBuilder ( )
2018-02-09 21:51:02 +00:00
. BeginRenderPass ( fb . renderPass , fb . framebuffer )
2017-08-01 18:20:43 +00:00
. BeginRenderSubpass ( )
// First use the base pipeline to draw a triangle with no blending
. SetRenderPipeline ( basePipeline )
. SetBindGroup ( 0 , MakeBindGroupForColors ( std : : array < RGBA8 , 1 > ( { { base } } ) ) )
. DrawArrays ( 3 , 1 , 0 , 0 )
// Then use the test pipeline to draw the test triangle with blending
. SetRenderPipeline ( testPipeline )
. SetBindGroup ( 0 , MakeBindGroupForColors ( std : : array < RGBA8 , 1 > ( { { triangle . color } } ) ) )
. SetBlendColor ( triangle . blendFactor [ 0 ] , triangle . blendFactor [ 1 ] , triangle . blendFactor [ 2 ] , triangle . blendFactor [ 3 ] )
. DrawArrays ( 3 , 1 , 0 , 0 )
. EndRenderSubpass ( )
. EndRenderPass ( )
. GetResult ( ) ;
queue . Submit ( 1 , & commands ) ;
2018-02-09 21:51:02 +00:00
EXPECT_PIXEL_RGBA8_EQ ( expected , fb . color , kRTSize / 2 , kRTSize / 2 ) ;
2017-08-01 18:20:43 +00:00
}
// Given a vector of tests where each element is <testColor, expectedColor>, check that all expectations are true for the given blend operation
void CheckBlendOperation ( RGBA8 base , nxt : : BlendOperation operation , std : : vector < std : : pair < RGBA8 , RGBA8 > > tests ) {
nxt : : BlendState blendState = device . CreateBlendStateBuilder ( )
. SetBlendEnabled ( true )
. SetColorBlend ( operation , nxt : : BlendFactor : : One , nxt : : BlendFactor : : One )
. SetAlphaBlend ( operation , nxt : : BlendFactor : : One , nxt : : BlendFactor : : One )
. GetResult ( ) ;
SetupSingleSourcePipelines ( blendState ) ;
for ( const auto & test : tests ) {
DoSingleSourceTest ( base , { test . first } , test . second ) ;
}
}
// Given a vector of tests where each element is <testSpec, expectedColor>, check that all expectations are true for the given blend factors
void CheckBlendFactor ( RGBA8 base , nxt : : BlendFactor colorSrcFactor , nxt : : BlendFactor colorDstFactor , nxt : : BlendFactor alphaSrcFactor , nxt : : BlendFactor alphaDstFactor , std : : vector < std : : pair < TriangleSpec , RGBA8 > > tests ) {
nxt : : BlendState blendState = device . CreateBlendStateBuilder ( )
. SetBlendEnabled ( true )
. SetColorBlend ( nxt : : BlendOperation : : Add , colorSrcFactor , colorDstFactor )
. SetAlphaBlend ( nxt : : BlendOperation : : Add , alphaSrcFactor , alphaDstFactor )
. GetResult ( ) ;
SetupSingleSourcePipelines ( blendState ) ;
for ( const auto & test : tests ) {
DoSingleSourceTest ( base , test . first , test . second ) ;
}
}
void CheckSrcBlendFactor ( RGBA8 base , nxt : : BlendFactor colorFactor , nxt : : BlendFactor alphaFactor , std : : vector < std : : pair < TriangleSpec , RGBA8 > > tests ) {
CheckBlendFactor ( base , colorFactor , nxt : : BlendFactor : : One , alphaFactor , nxt : : BlendFactor : : One , tests ) ;
}
void CheckDstBlendFactor ( RGBA8 base , nxt : : BlendFactor colorFactor , nxt : : BlendFactor alphaFactor , std : : vector < std : : pair < TriangleSpec , RGBA8 > > tests ) {
CheckBlendFactor ( base , nxt : : BlendFactor : : One , colorFactor , nxt : : BlendFactor : : One , alphaFactor , tests ) ;
}
2018-02-09 21:51:02 +00:00
utils : : BasicFramebuffer fb ;
2017-08-01 18:20:43 +00:00
nxt : : RenderPipeline basePipeline ;
nxt : : RenderPipeline testPipeline ;
nxt : : ShaderModule vsModule ;
nxt : : BindGroupLayout bindGroupLayout ;
nxt : : PipelineLayout pipelineLayout ;
} ;
namespace {
// Add two colors and clamp
constexpr RGBA8 operator + ( const RGBA8 & col1 , const RGBA8 & col2 ) {
int r = static_cast < int > ( col1 . r ) + static_cast < int > ( col2 . r ) ;
int g = static_cast < int > ( col1 . g ) + static_cast < int > ( col2 . g ) ;
int b = static_cast < int > ( col1 . b ) + static_cast < int > ( col2 . b ) ;
int a = static_cast < int > ( col1 . a ) + static_cast < int > ( col2 . a ) ;
r = ( r > 255 ? 255 : ( r < 0 ? 0 : r ) ) ;
g = ( g > 255 ? 255 : ( g < 0 ? 0 : g ) ) ;
b = ( b > 255 ? 255 : ( b < 0 ? 0 : b ) ) ;
a = ( a > 255 ? 255 : ( a < 0 ? 0 : a ) ) ;
return RGBA8 ( static_cast < uint8_t > ( r ) , static_cast < uint8_t > ( g ) , static_cast < uint8_t > ( b ) , static_cast < uint8_t > ( a ) ) ;
}
// Subtract two colors and clamp
constexpr RGBA8 operator - ( const RGBA8 & col1 , const RGBA8 & col2 ) {
int r = static_cast < int > ( col1 . r ) - static_cast < int > ( col2 . r ) ;
int g = static_cast < int > ( col1 . g ) - static_cast < int > ( col2 . g ) ;
int b = static_cast < int > ( col1 . b ) - static_cast < int > ( col2 . b ) ;
int a = static_cast < int > ( col1 . a ) - static_cast < int > ( col2 . a ) ;
r = ( r > 255 ? 255 : ( r < 0 ? 0 : r ) ) ;
g = ( g > 255 ? 255 : ( g < 0 ? 0 : g ) ) ;
b = ( b > 255 ? 255 : ( b < 0 ? 0 : b ) ) ;
a = ( a > 255 ? 255 : ( a < 0 ? 0 : a ) ) ;
return RGBA8 ( static_cast < uint8_t > ( r ) , static_cast < uint8_t > ( g ) , static_cast < uint8_t > ( b ) , static_cast < uint8_t > ( a ) ) ;
}
// Get the component-wise minimum of two colors
RGBA8 min ( const RGBA8 & col1 , const RGBA8 & col2 ) {
return RGBA8 (
std : : min ( col1 . r , col2 . r ) ,
std : : min ( col1 . g , col2 . g ) ,
std : : min ( col1 . b , col2 . b ) ,
std : : min ( col1 . a , col2 . a )
) ;
}
// Get the component-wise maximum of two colors
RGBA8 max ( const RGBA8 & col1 , const RGBA8 & col2 ) {
return RGBA8 (
std : : max ( col1 . r , col2 . r ) ,
std : : max ( col1 . g , col2 . g ) ,
std : : max ( col1 . b , col2 . b ) ,
std : : max ( col1 . a , col2 . a )
) ;
}
// Blend two RGBA8 color values parameterized by the provided factors in the range [0.f, 1.f]
RGBA8 mix ( const RGBA8 & col1 , const RGBA8 & col2 , std : : array < float , 4 > fac ) {
float r = static_cast < float > ( col1 . r ) * ( 1.f - fac [ 0 ] ) + static_cast < float > ( col2 . r ) * fac [ 0 ] ;
float g = static_cast < float > ( col1 . g ) * ( 1.f - fac [ 1 ] ) + static_cast < float > ( col2 . g ) * fac [ 1 ] ;
float b = static_cast < float > ( col1 . b ) * ( 1.f - fac [ 2 ] ) + static_cast < float > ( col2 . b ) * fac [ 2 ] ;
float a = static_cast < float > ( col1 . a ) * ( 1.f - fac [ 3 ] ) + static_cast < float > ( col2 . a ) * fac [ 3 ] ;
return RGBA8 ( { static_cast < uint8_t > ( std : : round ( r ) ) , static_cast < uint8_t > ( std : : round ( g ) ) , static_cast < uint8_t > ( std : : round ( b ) ) , static_cast < uint8_t > ( std : : round ( a ) ) } ) ;
}
// Blend two RGBA8 color values parameterized by the provided RGBA8 factor
RGBA8 mix ( const RGBA8 & col1 , const RGBA8 & col2 , const RGBA8 & fac ) {
std : : array < float , 4 > f = { {
static_cast < float > ( fac . r ) / 255.f ,
static_cast < float > ( fac . g ) / 255.f ,
static_cast < float > ( fac . b ) / 255.f ,
static_cast < float > ( fac . a ) / 255.f ,
} } ;
return mix ( col1 , col2 , f ) ;
}
constexpr std : : array < RGBA8 , 8 > kColors = { {
// check operations over multiple channels
RGBA8 ( 64 , 0 , 0 , 0 ) ,
RGBA8 ( 0 , 64 , 0 , 0 ) ,
RGBA8 ( 64 , 0 , 32 , 0 ) ,
RGBA8 ( 0 , 64 , 32 , 0 ) ,
RGBA8 ( 128 , 0 , 128 , 128 ) ,
RGBA8 ( 0 , 128 , 128 , 128 ) ,
// check cases that may cause overflow
RGBA8 ( 0 , 0 , 0 , 0 ) ,
RGBA8 ( 255 , 255 , 255 , 255 ) ,
} } ;
}
// Test compilation and usage of the fixture
TEST_P ( BlendStateTest , Basic ) {
nxt : : BlendState blendState = device . CreateBlendStateBuilder ( ) . GetResult ( ) ;
SetupSingleSourcePipelines ( blendState ) ;
DoSingleSourceTest ( RGBA8 ( 0 , 0 , 0 , 0 ) , { RGBA8 ( 255 , 0 , 0 , 0 ) } , RGBA8 ( 255 , 0 , 0 , 0 ) ) ;
}
// The following tests check test that the blend operation works
TEST_P ( BlendStateTest , BlendOperationAdd ) {
RGBA8 base ( 32 , 64 , 128 , 192 ) ;
std : : vector < std : : pair < RGBA8 , RGBA8 > > tests ;
std : : transform ( kColors . begin ( ) , kColors . end ( ) , std : : back_inserter ( tests ) , [ & ] ( const RGBA8 & color ) {
return std : : make_pair ( color , base + color ) ;
} ) ;
CheckBlendOperation ( base , nxt : : BlendOperation : : Add , tests ) ;
}
TEST_P ( BlendStateTest , BlendOperationSubtract ) {
RGBA8 base ( 32 , 64 , 128 , 192 ) ;
std : : vector < std : : pair < RGBA8 , RGBA8 > > tests ;
std : : transform ( kColors . begin ( ) , kColors . end ( ) , std : : back_inserter ( tests ) , [ & ] ( const RGBA8 & color ) {
return std : : make_pair ( color , color - base ) ;
} ) ;
CheckBlendOperation ( base , nxt : : BlendOperation : : Subtract , tests ) ;
}
TEST_P ( BlendStateTest , BlendOperationReverseSubtract ) {
RGBA8 base ( 32 , 64 , 128 , 192 ) ;
std : : vector < std : : pair < RGBA8 , RGBA8 > > tests ;
std : : transform ( kColors . begin ( ) , kColors . end ( ) , std : : back_inserter ( tests ) , [ & ] ( const RGBA8 & color ) {
return std : : make_pair ( color , base - color ) ;
} ) ;
CheckBlendOperation ( base , nxt : : BlendOperation : : ReverseSubtract , tests ) ;
}
TEST_P ( BlendStateTest , BlendOperationMin ) {
RGBA8 base ( 32 , 64 , 128 , 192 ) ;
std : : vector < std : : pair < RGBA8 , RGBA8 > > tests ;
std : : transform ( kColors . begin ( ) , kColors . end ( ) , std : : back_inserter ( tests ) , [ & ] ( const RGBA8 & color ) {
return std : : make_pair ( color , min ( base , color ) ) ;
} ) ;
CheckBlendOperation ( base , nxt : : BlendOperation : : Min , tests ) ;
}
TEST_P ( BlendStateTest , BlendOperationMax ) {
RGBA8 base ( 32 , 64 , 128 , 192 ) ;
std : : vector < std : : pair < RGBA8 , RGBA8 > > tests ;
std : : transform ( kColors . begin ( ) , kColors . end ( ) , std : : back_inserter ( tests ) , [ & ] ( const RGBA8 & color ) {
return std : : make_pair ( color , max ( base , color ) ) ;
} ) ;
CheckBlendOperation ( base , nxt : : BlendOperation : : Max , tests ) ;
}
// The following tests check that the Source blend factor works
TEST_P ( BlendStateTest , SrcBlendFactorZero ) {
RGBA8 base ( 32 , 64 , 128 , 192 ) ;
std : : vector < std : : pair < TriangleSpec , RGBA8 > > tests ;
std : : transform ( kColors . begin ( ) , kColors . end ( ) , std : : back_inserter ( tests ) , [ & ] ( const RGBA8 & color ) {
return std : : make_pair ( TriangleSpec ( { { color } } ) , base ) ;
} ) ;
CheckSrcBlendFactor ( base , nxt : : BlendFactor : : Zero , nxt : : BlendFactor : : Zero , tests ) ;
}
TEST_P ( BlendStateTest , SrcBlendFactorOne ) {
RGBA8 base ( 32 , 64 , 128 , 192 ) ;
std : : vector < std : : pair < TriangleSpec , RGBA8 > > tests ;
std : : transform ( kColors . begin ( ) , kColors . end ( ) , std : : back_inserter ( tests ) , [ & ] ( const RGBA8 & color ) {
return std : : make_pair ( TriangleSpec ( { { color } } ) , base + color ) ;
} ) ;
CheckSrcBlendFactor ( base , nxt : : BlendFactor : : One , nxt : : BlendFactor : : One , tests ) ;
}
TEST_P ( BlendStateTest , SrcBlendFactorSrcColor ) {
RGBA8 base ( 32 , 64 , 128 , 192 ) ;
std : : vector < std : : pair < TriangleSpec , RGBA8 > > tests ;
std : : transform ( kColors . begin ( ) , kColors . end ( ) , std : : back_inserter ( tests ) , [ & ] ( const RGBA8 & color ) {
RGBA8 fac = color ;
fac . a = 0 ;
RGBA8 expected = base + mix ( RGBA8 ( 0 , 0 , 0 , 0 ) , color , fac ) ;
return std : : make_pair ( TriangleSpec ( { { color } } ) , expected ) ;
} ) ;
CheckSrcBlendFactor ( base , nxt : : BlendFactor : : SrcColor , nxt : : BlendFactor : : Zero , tests ) ;
}
TEST_P ( BlendStateTest , SrcBlendFactorOneMinusSrcColor ) {
RGBA8 base ( 32 , 64 , 128 , 192 ) ;
std : : vector < std : : pair < TriangleSpec , RGBA8 > > tests ;
std : : transform ( kColors . begin ( ) , kColors . end ( ) , std : : back_inserter ( tests ) , [ & ] ( const RGBA8 & color ) {
RGBA8 fac = RGBA8 ( 255 , 255 , 255 , 255 ) - color ;
fac . a = 0 ;
RGBA8 expected = base + mix ( RGBA8 ( 0 , 0 , 0 , 0 ) , color , fac ) ;
return std : : make_pair ( TriangleSpec ( { { color } } ) , expected ) ;
} ) ;
CheckSrcBlendFactor ( base , nxt : : BlendFactor : : OneMinusSrcColor , nxt : : BlendFactor : : Zero , tests ) ;
}
TEST_P ( BlendStateTest , SrcBlendFactorSrcAlpha ) {
RGBA8 base ( 32 , 64 , 128 , 192 ) ;
std : : vector < std : : pair < TriangleSpec , RGBA8 > > tests ;
std : : transform ( kColors . begin ( ) , kColors . end ( ) , std : : back_inserter ( tests ) , [ & ] ( const RGBA8 & color ) {
RGBA8 fac ( color . a , color . a , color . a , color . a ) ;
RGBA8 expected = base + mix ( RGBA8 ( 0 , 0 , 0 , 0 ) , color , fac ) ;
return std : : make_pair ( TriangleSpec ( { { color } } ) , expected ) ;
} ) ;
CheckSrcBlendFactor ( base , nxt : : BlendFactor : : SrcAlpha , nxt : : BlendFactor : : SrcAlpha , tests ) ;
}
TEST_P ( BlendStateTest , SrcBlendFactorOneMinusSrcAlpha ) {
RGBA8 base ( 32 , 64 , 128 , 192 ) ;
std : : vector < std : : pair < TriangleSpec , RGBA8 > > tests ;
std : : transform ( kColors . begin ( ) , kColors . end ( ) , std : : back_inserter ( tests ) , [ & ] ( const RGBA8 & color ) {
RGBA8 fac = RGBA8 ( 255 , 255 , 255 , 255 ) - RGBA8 ( color . a , color . a , color . a , color . a ) ;
RGBA8 expected = base + mix ( RGBA8 ( 0 , 0 , 0 , 0 ) , color , fac ) ;
return std : : make_pair ( TriangleSpec ( { { color } } ) , expected ) ;
} ) ;
CheckSrcBlendFactor ( base , nxt : : BlendFactor : : OneMinusSrcAlpha , nxt : : BlendFactor : : OneMinusSrcAlpha , tests ) ;
}
TEST_P ( BlendStateTest , SrcBlendFactorDstColor ) {
RGBA8 base ( 32 , 64 , 128 , 192 ) ;
std : : vector < std : : pair < TriangleSpec , RGBA8 > > tests ;
std : : transform ( kColors . begin ( ) , kColors . end ( ) , std : : back_inserter ( tests ) , [ & ] ( const RGBA8 & color ) {
RGBA8 fac = base ;
fac . a = 0 ;
RGBA8 expected = base + mix ( RGBA8 ( 0 , 0 , 0 , 0 ) , color , fac ) ;
return std : : make_pair ( TriangleSpec ( { { color } } ) , expected ) ;
} ) ;
CheckSrcBlendFactor ( base , nxt : : BlendFactor : : DstColor , nxt : : BlendFactor : : Zero , tests ) ;
}
TEST_P ( BlendStateTest , SrcBlendFactorOneMinusDstColor ) {
RGBA8 base ( 32 , 64 , 128 , 192 ) ;
std : : vector < std : : pair < TriangleSpec , RGBA8 > > tests ;
std : : transform ( kColors . begin ( ) , kColors . end ( ) , std : : back_inserter ( tests ) , [ & ] ( const RGBA8 & color ) {
RGBA8 fac = RGBA8 ( 255 , 255 , 255 , 255 ) - base ;
fac . a = 0 ;
RGBA8 expected = base + mix ( RGBA8 ( 0 , 0 , 0 , 0 ) , color , fac ) ;
return std : : make_pair ( TriangleSpec ( { { color } } ) , expected ) ;
} ) ;
CheckSrcBlendFactor ( base , nxt : : BlendFactor : : OneMinusDstColor , nxt : : BlendFactor : : Zero , tests ) ;
}
TEST_P ( BlendStateTest , SrcBlendFactorDstAlpha ) {
RGBA8 base ( 32 , 64 , 128 , 192 ) ;
std : : vector < std : : pair < TriangleSpec , RGBA8 > > tests ;
std : : transform ( kColors . begin ( ) , kColors . end ( ) , std : : back_inserter ( tests ) , [ & ] ( const RGBA8 & color ) {
RGBA8 fac ( base . a , base . a , base . a , base . a ) ;
RGBA8 expected = base + mix ( RGBA8 ( 0 , 0 , 0 , 0 ) , color , fac ) ;
return std : : make_pair ( TriangleSpec ( { { color } } ) , expected ) ;
} ) ;
CheckSrcBlendFactor ( base , nxt : : BlendFactor : : DstAlpha , nxt : : BlendFactor : : DstAlpha , tests ) ;
}
TEST_P ( BlendStateTest , SrcBlendFactorOneMinusDstAlpha ) {
RGBA8 base ( 32 , 64 , 128 , 192 ) ;
std : : vector < std : : pair < TriangleSpec , RGBA8 > > tests ;
std : : transform ( kColors . begin ( ) , kColors . end ( ) , std : : back_inserter ( tests ) , [ & ] ( const RGBA8 & color ) {
RGBA8 fac = RGBA8 ( 255 , 255 , 255 , 255 ) - RGBA8 ( base . a , base . a , base . a , base . a ) ;
RGBA8 expected = base + mix ( RGBA8 ( 0 , 0 , 0 , 0 ) , color , fac ) ;
return std : : make_pair ( TriangleSpec ( { { color } } ) , expected ) ;
} ) ;
CheckSrcBlendFactor ( base , nxt : : BlendFactor : : OneMinusDstAlpha , nxt : : BlendFactor : : OneMinusDstAlpha , tests ) ;
}
TEST_P ( BlendStateTest , SrcBlendFactorSrcAlphaSaturated ) {
RGBA8 base ( 32 , 64 , 128 , 192 ) ;
std : : vector < std : : pair < TriangleSpec , RGBA8 > > tests ;
std : : transform ( kColors . begin ( ) , kColors . end ( ) , std : : back_inserter ( tests ) , [ & ] ( const RGBA8 & color ) {
uint8_t f = std : : min ( color . a , static_cast < uint8_t > ( 255 - base . a ) ) ;
RGBA8 fac ( f , f , f , 255 ) ;
RGBA8 expected = base + mix ( RGBA8 ( 0 , 0 , 0 , 0 ) , color , fac ) ;
return std : : make_pair ( TriangleSpec ( { { color } } ) , expected ) ;
} ) ;
CheckSrcBlendFactor ( base , nxt : : BlendFactor : : SrcAlphaSaturated , nxt : : BlendFactor : : SrcAlphaSaturated , tests ) ;
}
TEST_P ( BlendStateTest , SrcBlendFactorBlendColor ) {
RGBA8 base ( 32 , 64 , 128 , 192 ) ;
std : : vector < std : : pair < TriangleSpec , RGBA8 > > tests ;
std : : transform ( kColors . begin ( ) , kColors . end ( ) , std : : back_inserter ( tests ) , [ & ] ( const RGBA8 & color ) {
auto triangleSpec = TriangleSpec ( { { color } , { { 0.2f , 0.4f , 0.6f , 0.8f } } } ) ;
RGBA8 expected = base + mix ( RGBA8 ( 0 , 0 , 0 , 0 ) , color , triangleSpec . blendFactor ) ;
return std : : make_pair ( triangleSpec , expected ) ;
} ) ;
CheckSrcBlendFactor ( base , nxt : : BlendFactor : : BlendColor , nxt : : BlendFactor : : BlendColor , tests ) ;
}
TEST_P ( BlendStateTest , SrcBlendFactorOneMinusBlendColor ) {
RGBA8 base ( 32 , 64 , 128 , 192 ) ;
std : : vector < std : : pair < TriangleSpec , RGBA8 > > tests ;
std : : transform ( kColors . begin ( ) , kColors . end ( ) , std : : back_inserter ( tests ) , [ & ] ( const RGBA8 & color ) {
auto triangleSpec = TriangleSpec ( { { color } , { { 0.2f , 0.4f , 0.6f , 0.8f } } } ) ;
std : : array < float , 4 > f = { { 0.8f , 0.6f , 0.4f , 0.2f } } ;
RGBA8 expected = base + mix ( RGBA8 ( 0 , 0 , 0 , 0 ) , color , f ) ;
return std : : make_pair ( triangleSpec , expected ) ;
} ) ;
CheckSrcBlendFactor ( base , nxt : : BlendFactor : : OneMinusBlendColor , nxt : : BlendFactor : : OneMinusBlendColor , tests ) ;
}
// The following tests check that the Destination blend factor works
TEST_P ( BlendStateTest , DstBlendFactorZero ) {
RGBA8 base ( 32 , 64 , 128 , 192 ) ;
std : : vector < std : : pair < TriangleSpec , RGBA8 > > tests ;
std : : transform ( kColors . begin ( ) , kColors . end ( ) , std : : back_inserter ( tests ) , [ & ] ( const RGBA8 & color ) {
return std : : make_pair ( TriangleSpec ( { { color } } ) , color ) ;
} ) ;
CheckDstBlendFactor ( base , nxt : : BlendFactor : : Zero , nxt : : BlendFactor : : Zero , tests ) ;
}
TEST_P ( BlendStateTest , DstBlendFactorOne ) {
RGBA8 base ( 32 , 64 , 128 , 192 ) ;
std : : vector < std : : pair < TriangleSpec , RGBA8 > > tests ;
std : : transform ( kColors . begin ( ) , kColors . end ( ) , std : : back_inserter ( tests ) , [ & ] ( const RGBA8 & color ) {
return std : : make_pair ( TriangleSpec ( { { color } } ) , base + color ) ;
} ) ;
CheckDstBlendFactor ( base , nxt : : BlendFactor : : One , nxt : : BlendFactor : : One , tests ) ;
}
TEST_P ( BlendStateTest , DstBlendFactorSrcColor ) {
RGBA8 base ( 32 , 64 , 128 , 192 ) ;
std : : vector < std : : pair < TriangleSpec , RGBA8 > > tests ;
std : : transform ( kColors . begin ( ) , kColors . end ( ) , std : : back_inserter ( tests ) , [ & ] ( const RGBA8 & color ) {
RGBA8 fac = color ;
fac . a = 0 ;
RGBA8 expected = color + mix ( RGBA8 ( 0 , 0 , 0 , 0 ) , base , fac ) ;
return std : : make_pair ( TriangleSpec ( { { color } } ) , expected ) ;
} ) ;
CheckDstBlendFactor ( base , nxt : : BlendFactor : : SrcColor , nxt : : BlendFactor : : Zero , tests ) ;
}
TEST_P ( BlendStateTest , DstBlendFactorOneMinusSrcColor ) {
RGBA8 base ( 32 , 64 , 128 , 192 ) ;
std : : vector < std : : pair < TriangleSpec , RGBA8 > > tests ;
std : : transform ( kColors . begin ( ) , kColors . end ( ) , std : : back_inserter ( tests ) , [ & ] ( const RGBA8 & color ) {
RGBA8 fac = RGBA8 ( 255 , 255 , 255 , 255 ) - color ;
fac . a = 0 ;
RGBA8 expected = color + mix ( RGBA8 ( 0 , 0 , 0 , 0 ) , base , fac ) ;
return std : : make_pair ( TriangleSpec ( { { color } } ) , expected ) ;
} ) ;
CheckDstBlendFactor ( base , nxt : : BlendFactor : : OneMinusSrcColor , nxt : : BlendFactor : : Zero , tests ) ;
}
TEST_P ( BlendStateTest , DstBlendFactorSrcAlpha ) {
RGBA8 base ( 32 , 64 , 128 , 192 ) ;
std : : vector < std : : pair < TriangleSpec , RGBA8 > > tests ;
std : : transform ( kColors . begin ( ) , kColors . end ( ) , std : : back_inserter ( tests ) , [ & ] ( const RGBA8 & color ) {
RGBA8 fac ( color . a , color . a , color . a , color . a ) ;
RGBA8 expected = color + mix ( RGBA8 ( 0 , 0 , 0 , 0 ) , base , fac ) ;
return std : : make_pair ( TriangleSpec ( { { color } } ) , expected ) ;
} ) ;
CheckDstBlendFactor ( base , nxt : : BlendFactor : : SrcAlpha , nxt : : BlendFactor : : SrcAlpha , tests ) ;
}
TEST_P ( BlendStateTest , DstBlendFactorOneMinusSrcAlpha ) {
RGBA8 base ( 32 , 64 , 128 , 192 ) ;
std : : vector < std : : pair < TriangleSpec , RGBA8 > > tests ;
std : : transform ( kColors . begin ( ) , kColors . end ( ) , std : : back_inserter ( tests ) , [ & ] ( const RGBA8 & color ) {
RGBA8 fac = RGBA8 ( 255 , 255 , 255 , 255 ) - RGBA8 ( color . a , color . a , color . a , color . a ) ;
RGBA8 expected = color + mix ( RGBA8 ( 0 , 0 , 0 , 0 ) , base , fac ) ;
return std : : make_pair ( TriangleSpec ( { { color } } ) , expected ) ;
} ) ;
CheckDstBlendFactor ( base , nxt : : BlendFactor : : OneMinusSrcAlpha , nxt : : BlendFactor : : OneMinusSrcAlpha , tests ) ;
}
TEST_P ( BlendStateTest , DstBlendFactorDstColor ) {
RGBA8 base ( 32 , 64 , 128 , 192 ) ;
std : : vector < std : : pair < TriangleSpec , RGBA8 > > tests ;
std : : transform ( kColors . begin ( ) , kColors . end ( ) , std : : back_inserter ( tests ) , [ & ] ( const RGBA8 & color ) {
RGBA8 fac = base ;
fac . a = 0 ;
RGBA8 expected = color + mix ( RGBA8 ( 0 , 0 , 0 , 0 ) , base , fac ) ;
return std : : make_pair ( TriangleSpec ( { { color } } ) , expected ) ;
} ) ;
CheckDstBlendFactor ( base , nxt : : BlendFactor : : DstColor , nxt : : BlendFactor : : Zero , tests ) ;
}
TEST_P ( BlendStateTest , DstBlendFactorOneMinusDstColor ) {
RGBA8 base ( 32 , 64 , 128 , 192 ) ;
std : : vector < std : : pair < TriangleSpec , RGBA8 > > tests ;
std : : transform ( kColors . begin ( ) , kColors . end ( ) , std : : back_inserter ( tests ) , [ & ] ( const RGBA8 & color ) {
RGBA8 fac = RGBA8 ( 255 , 255 , 255 , 255 ) - base ;
fac . a = 0 ;
RGBA8 expected = color + mix ( RGBA8 ( 0 , 0 , 0 , 0 ) , base , fac ) ;
return std : : make_pair ( TriangleSpec ( { { color } } ) , expected ) ;
} ) ;
CheckDstBlendFactor ( base , nxt : : BlendFactor : : OneMinusDstColor , nxt : : BlendFactor : : Zero , tests ) ;
}
TEST_P ( BlendStateTest , DstBlendFactorDstAlpha ) {
RGBA8 base ( 32 , 64 , 128 , 192 ) ;
std : : vector < std : : pair < TriangleSpec , RGBA8 > > tests ;
std : : transform ( kColors . begin ( ) , kColors . end ( ) , std : : back_inserter ( tests ) , [ & ] ( const RGBA8 & color ) {
RGBA8 fac ( base . a , base . a , base . a , base . a ) ;
RGBA8 expected = color + mix ( RGBA8 ( 0 , 0 , 0 , 0 ) , base , fac ) ;
return std : : make_pair ( TriangleSpec ( { { color } } ) , expected ) ;
} ) ;
CheckDstBlendFactor ( base , nxt : : BlendFactor : : DstAlpha , nxt : : BlendFactor : : DstAlpha , tests ) ;
}
TEST_P ( BlendStateTest , DstBlendFactorOneMinusDstAlpha ) {
RGBA8 base ( 32 , 64 , 128 , 192 ) ;
std : : vector < std : : pair < TriangleSpec , RGBA8 > > tests ;
std : : transform ( kColors . begin ( ) , kColors . end ( ) , std : : back_inserter ( tests ) , [ & ] ( const RGBA8 & color ) {
RGBA8 fac = RGBA8 ( 255 , 255 , 255 , 255 ) - RGBA8 ( base . a , base . a , base . a , base . a ) ;
RGBA8 expected = color + mix ( RGBA8 ( 0 , 0 , 0 , 0 ) , base , fac ) ;
return std : : make_pair ( TriangleSpec ( { { color } } ) , expected ) ;
} ) ;
CheckDstBlendFactor ( base , nxt : : BlendFactor : : OneMinusDstAlpha , nxt : : BlendFactor : : OneMinusDstAlpha , tests ) ;
}
TEST_P ( BlendStateTest , DstBlendFactorSrcAlphaSaturated ) {
RGBA8 base ( 32 , 64 , 128 , 192 ) ;
std : : vector < std : : pair < TriangleSpec , RGBA8 > > tests ;
std : : transform ( kColors . begin ( ) , kColors . end ( ) , std : : back_inserter ( tests ) , [ & ] ( const RGBA8 & color ) {
uint8_t f = std : : min ( color . a , static_cast < uint8_t > ( 255 - base . a ) ) ;
RGBA8 fac ( f , f , f , 255 ) ;
RGBA8 expected = color + mix ( RGBA8 ( 0 , 0 , 0 , 0 ) , base , fac ) ;
return std : : make_pair ( TriangleSpec ( { { color } } ) , expected ) ;
} ) ;
CheckDstBlendFactor ( base , nxt : : BlendFactor : : SrcAlphaSaturated , nxt : : BlendFactor : : SrcAlphaSaturated , tests ) ;
}
TEST_P ( BlendStateTest , DstBlendFactorBlendColor ) {
RGBA8 base ( 32 , 64 , 128 , 192 ) ;
std : : vector < std : : pair < TriangleSpec , RGBA8 > > tests ;
std : : transform ( kColors . begin ( ) , kColors . end ( ) , std : : back_inserter ( tests ) , [ & ] ( const RGBA8 & color ) {
auto triangleSpec = TriangleSpec ( { { color } , { { 0.2f , 0.4f , 0.6f , 0.8f } } } ) ;
RGBA8 expected = color + mix ( RGBA8 ( 0 , 0 , 0 , 0 ) , base , triangleSpec . blendFactor ) ;
return std : : make_pair ( triangleSpec , expected ) ;
} ) ;
CheckDstBlendFactor ( base , nxt : : BlendFactor : : BlendColor , nxt : : BlendFactor : : BlendColor , tests ) ;
}
TEST_P ( BlendStateTest , DstBlendFactorOneMinusBlendColor ) {
RGBA8 base ( 32 , 64 , 128 , 192 ) ;
std : : vector < std : : pair < TriangleSpec , RGBA8 > > tests ;
std : : transform ( kColors . begin ( ) , kColors . end ( ) , std : : back_inserter ( tests ) , [ & ] ( const RGBA8 & color ) {
auto triangleSpec = TriangleSpec ( { { color } , { { 0.2f , 0.4f , 0.6f , 0.8f } } } ) ;
std : : array < float , 4 > f = { { 0.8f , 0.6f , 0.4f , 0.2f } } ;
RGBA8 expected = color + mix ( RGBA8 ( 0 , 0 , 0 , 0 ) , base , f ) ;
return std : : make_pair ( triangleSpec , expected ) ;
} ) ;
CheckDstBlendFactor ( base , nxt : : BlendFactor : : OneMinusBlendColor , nxt : : BlendFactor : : OneMinusBlendColor , tests ) ;
}
// Check that the color write mask works
TEST_P ( BlendStateTest , ColorWriteMask ) {
{
// Test single channel color write
nxt : : BlendState blendState = device . CreateBlendStateBuilder ( )
. SetBlendEnabled ( true )
. SetColorBlend ( nxt : : BlendOperation : : Add , nxt : : BlendFactor : : One , nxt : : BlendFactor : : One )
. SetAlphaBlend ( nxt : : BlendOperation : : Add , nxt : : BlendFactor : : One , nxt : : BlendFactor : : One )
. SetColorWriteMask ( nxt : : ColorWriteMask : : Red )
. GetResult ( ) ;
SetupSingleSourcePipelines ( blendState ) ;
RGBA8 base ( 32 , 64 , 128 , 192 ) ;
for ( auto & color : kColors ) {
RGBA8 expected = base + RGBA8 ( color . r , 0 , 0 , 0 ) ;
DoSingleSourceTest ( base , { color } , expected ) ;
}
}
{
// Test multi channel color write
nxt : : BlendState blendState = device . CreateBlendStateBuilder ( )
. SetBlendEnabled ( true )
. SetColorBlend ( nxt : : BlendOperation : : Add , nxt : : BlendFactor : : One , nxt : : BlendFactor : : One )
. SetAlphaBlend ( nxt : : BlendOperation : : Add , nxt : : BlendFactor : : One , nxt : : BlendFactor : : One )
. SetColorWriteMask ( nxt : : ColorWriteMask : : Green | nxt : : ColorWriteMask : : Alpha )
. GetResult ( ) ;
SetupSingleSourcePipelines ( blendState ) ;
RGBA8 base ( 32 , 64 , 128 , 192 ) ;
for ( auto & color : kColors ) {
RGBA8 expected = base + RGBA8 ( 0 , color . g , 0 , color . a ) ;
DoSingleSourceTest ( base , { color } , expected ) ;
}
}
{
// Test no channel color write
nxt : : BlendState blendState = device . CreateBlendStateBuilder ( )
. SetBlendEnabled ( true )
. SetColorBlend ( nxt : : BlendOperation : : Add , nxt : : BlendFactor : : One , nxt : : BlendFactor : : One )
. SetAlphaBlend ( nxt : : BlendOperation : : Add , nxt : : BlendFactor : : One , nxt : : BlendFactor : : One )
. SetColorWriteMask ( nxt : : ColorWriteMask : : None )
. GetResult ( ) ;
SetupSingleSourcePipelines ( blendState ) ;
RGBA8 base ( 32 , 64 , 128 , 192 ) ;
for ( auto & color : kColors ) {
DoSingleSourceTest ( base , { color } , base ) ;
}
}
}
2018-03-01 20:41:48 +00:00
// Check that the color write mask works when blending is disabled
TEST_P ( BlendStateTest , ColorWriteMaskBlendingDisabled ) {
{
nxt : : BlendState blendState = device . CreateBlendStateBuilder ( )
. SetBlendEnabled ( false )
. SetColorWriteMask ( nxt : : ColorWriteMask : : Red )
. GetResult ( ) ;
SetupSingleSourcePipelines ( blendState ) ;
RGBA8 base ( 32 , 64 , 128 , 192 ) ;
RGBA8 expected ( 32 , 0 , 0 , 0 ) ;
nxt : : CommandBuffer commands = device . CreateCommandBufferBuilder ( )
. BeginRenderPass ( fb . renderPass , fb . framebuffer )
. BeginRenderSubpass ( )
. SetRenderPipeline ( testPipeline )
. SetBindGroup ( 0 , MakeBindGroupForColors ( std : : array < RGBA8 , 1 > ( { { base } } ) ) )
. DrawArrays ( 3 , 1 , 0 , 0 )
. EndRenderSubpass ( )
. EndRenderPass ( )
. GetResult ( ) ;
queue . Submit ( 1 , & commands ) ;
EXPECT_PIXEL_RGBA8_EQ ( expected , fb . color , kRTSize / 2 , kRTSize / 2 ) ;
}
}
2017-08-01 18:20:43 +00:00
// Test that independent blend states on render targets works
TEST_P ( BlendStateTest , IndependentBlendState ) {
std : : array < nxt : : Texture , 5 > renderTargets ;
std : : array < nxt : : TextureView , 5 > renderTargetViews ;
for ( uint32_t i = 0 ; i < 5 ; + + i ) {
renderTargets [ i ] = device . CreateTextureBuilder ( )
. SetDimension ( nxt : : TextureDimension : : e2D )
. SetExtent ( kRTSize , kRTSize , 1 )
. SetFormat ( nxt : : TextureFormat : : R8G8B8A8Unorm )
. SetMipLevels ( 1 )
. SetAllowedUsage ( nxt : : TextureUsageBit : : OutputAttachment | nxt : : TextureUsageBit : : TransferSrc )
. SetInitialUsage ( nxt : : TextureUsageBit : : OutputAttachment )
. GetResult ( ) ;
renderTargetViews [ i ] = renderTargets [ i ] . CreateTextureViewBuilder ( ) . GetResult ( ) ;
}
2018-02-09 21:51:02 +00:00
nxt : : RenderPass renderpass = device . CreateRenderPassBuilder ( )
2017-08-01 18:20:43 +00:00
. SetAttachmentCount ( 5 )
. SetSubpassCount ( 1 )
. AttachmentSetFormat ( 0 , nxt : : TextureFormat : : R8G8B8A8Unorm )
. AttachmentSetFormat ( 1 , nxt : : TextureFormat : : R8G8B8A8Unorm )
. AttachmentSetFormat ( 2 , nxt : : TextureFormat : : R8G8B8A8Unorm )
. AttachmentSetFormat ( 3 , nxt : : TextureFormat : : R8G8B8A8Unorm )
. AttachmentSetFormat ( 4 , nxt : : TextureFormat : : R8G8B8A8Unorm )
// Scatter these so we know indexing to the right shader location is working
. SubpassSetColorAttachment ( 0 , 0 , 2 )
// We skip attachment index 1 to check the case where the blend states in the pipeline state are not tightly packed
. SubpassSetColorAttachment ( 0 , 1 , 4 )
. SubpassSetColorAttachment ( 0 , 2 , 3 )
. SubpassSetColorAttachment ( 0 , 3 , 0 )
. GetResult ( ) ;
2018-02-09 21:51:02 +00:00
nxt : : Framebuffer framebuffer = device . CreateFramebufferBuilder ( )
2017-08-01 18:20:43 +00:00
. SetRenderPass ( renderpass )
. SetDimensions ( kRTSize , kRTSize )
. SetAttachment ( 0 , renderTargetViews [ 0 ] )
. SetAttachment ( 1 , renderTargetViews [ 1 ] )
. SetAttachment ( 2 , renderTargetViews [ 2 ] )
. SetAttachment ( 3 , renderTargetViews [ 3 ] )
. SetAttachment ( 4 , renderTargetViews [ 4 ] )
. GetResult ( ) ;
nxt : : ShaderModule fsModule = utils : : CreateShaderModule ( device , nxt : : ShaderStage : : Fragment , R " (
# version 450
layout ( set = 0 , binding = 0 ) uniform myBlock {
vec4 color0 ;
vec4 color1 ;
vec4 color2 ;
vec4 color3 ;
} myUbo ;
layout ( location = 0 ) out vec4 fragColor0 ;
layout ( location = 1 ) out vec4 fragColor1 ;
layout ( location = 2 ) out vec4 fragColor2 ;
layout ( location = 3 ) out vec4 fragColor3 ;
void main ( ) {
fragColor0 = myUbo . color0 ;
fragColor1 = myUbo . color1 ;
fragColor2 = myUbo . color2 ;
fragColor3 = myUbo . color3 ;
}
) " );
std : : array < nxt : : BlendState , 3 > blendStates = { {
device . CreateBlendStateBuilder ( )
. SetBlendEnabled ( true )
. SetColorBlend ( nxt : : BlendOperation : : Add , nxt : : BlendFactor : : One , nxt : : BlendFactor : : One )
. SetAlphaBlend ( nxt : : BlendOperation : : Add , nxt : : BlendFactor : : One , nxt : : BlendFactor : : One )
. GetResult ( ) ,
device . CreateBlendStateBuilder ( )
. SetBlendEnabled ( true )
. SetColorBlend ( nxt : : BlendOperation : : Subtract , nxt : : BlendFactor : : One , nxt : : BlendFactor : : One )
. SetAlphaBlend ( nxt : : BlendOperation : : Subtract , nxt : : BlendFactor : : One , nxt : : BlendFactor : : One )
. GetResult ( ) ,
device . CreateBlendStateBuilder ( )
. SetBlendEnabled ( true )
. SetColorBlend ( nxt : : BlendOperation : : Min , nxt : : BlendFactor : : One , nxt : : BlendFactor : : One )
. SetAlphaBlend ( nxt : : BlendOperation : : Min , nxt : : BlendFactor : : One , nxt : : BlendFactor : : One )
. GetResult ( ) ,
} } ;
basePipeline = device . CreateRenderPipelineBuilder ( )
. SetSubpass ( renderpass , 0 )
. SetLayout ( pipelineLayout )
. SetStage ( nxt : : ShaderStage : : Vertex , vsModule , " main " )
. SetStage ( nxt : : ShaderStage : : Fragment , fsModule , " main " )
. GetResult ( ) ;
testPipeline = device . CreateRenderPipelineBuilder ( )
. SetSubpass ( renderpass , 0 )
. SetLayout ( pipelineLayout )
. SetStage ( nxt : : ShaderStage : : Vertex , vsModule , " main " )
. SetStage ( nxt : : ShaderStage : : Fragment , fsModule , " main " )
. SetColorAttachmentBlendState ( 0 , blendStates [ 0 ] )
. SetColorAttachmentBlendState ( 1 , blendStates [ 1 ] )
// Blend state not set on third color attachment. It should be default
. SetColorAttachmentBlendState ( 3 , blendStates [ 2 ] )
. GetResult ( ) ;
for ( unsigned int c = 0 ; c < kColors . size ( ) ; + + c ) {
RGBA8 base = kColors [ ( ( c + 31 ) * 29 ) % kColors . size ( ) ] ;
RGBA8 color0 = kColors [ ( ( c + 19 ) * 13 ) % kColors . size ( ) ] ;
RGBA8 color1 = kColors [ ( ( c + 11 ) * 43 ) % kColors . size ( ) ] ;
RGBA8 color2 = kColors [ ( ( c + 7 ) * 3 ) % kColors . size ( ) ] ;
RGBA8 color3 = kColors [ ( ( c + 13 ) * 71 ) % kColors . size ( ) ] ;
RGBA8 expected0 = color0 + base ;
RGBA8 expected1 = color1 - base ;
RGBA8 expected2 = color2 ;
RGBA8 expected3 = min ( color3 , base ) ;
renderTargets [ 2 ] . TransitionUsage ( nxt : : TextureUsageBit : : OutputAttachment ) ;
renderTargets [ 4 ] . TransitionUsage ( nxt : : TextureUsageBit : : OutputAttachment ) ;
renderTargets [ 3 ] . TransitionUsage ( nxt : : TextureUsageBit : : OutputAttachment ) ;
renderTargets [ 0 ] . TransitionUsage ( nxt : : TextureUsageBit : : OutputAttachment ) ;
nxt : : CommandBuffer commands = device . CreateCommandBufferBuilder ( )
. BeginRenderPass ( renderpass , framebuffer )
. BeginRenderSubpass ( )
. SetRenderPipeline ( basePipeline )
. SetBindGroup ( 0 , MakeBindGroupForColors ( std : : array < RGBA8 , 4 > ( { { base , base , base , base } } ) ) )
. DrawArrays ( 3 , 1 , 0 , 0 )
. SetRenderPipeline ( testPipeline )
. SetBindGroup ( 0 , MakeBindGroupForColors ( std : : array < RGBA8 , 4 > ( { { color0 , color1 , color2 , color3 } } ) ) )
. DrawArrays ( 3 , 1 , 0 , 0 )
. EndRenderSubpass ( )
. EndRenderPass ( )
. GetResult ( ) ;
queue . Submit ( 1 , & commands ) ;
EXPECT_PIXEL_RGBA8_EQ ( expected0 , renderTargets [ 2 ] , kRTSize / 2 , kRTSize / 2 ) < < " Attachment slot 0 using render target 2 should have been " < < color0 < < " + " < < base < < " = " < < expected0 ;
EXPECT_PIXEL_RGBA8_EQ ( expected1 , renderTargets [ 4 ] , kRTSize / 2 , kRTSize / 2 ) < < " Attachment slot 1 using render target 4 should have been " < < color1 < < " - " < < base < < " = " < < expected1 ;
EXPECT_PIXEL_RGBA8_EQ ( expected2 , renderTargets [ 3 ] , kRTSize / 2 , kRTSize / 2 ) < < " Attachment slot 2 using render target 3 should have been " < < color2 < < " = " < < expected2 < < " (no blending) " ;
EXPECT_PIXEL_RGBA8_EQ ( expected3 , renderTargets [ 0 ] , kRTSize / 2 , kRTSize / 2 ) < < " Attachment slot 3 using render target 0 should have been min( " < < color3 < < " , " < < base < < " ) = " < < expected3 ;
}
}
2018-02-02 22:32:28 +00:00
// Test that the default blend color is correctly set at the beginning of every subpass
2017-08-04 15:11:26 +00:00
TEST_P ( BlendStateTest , DefaultBlendColor ) {
2018-02-02 22:32:28 +00:00
if ( IsVulkan ( ) ) {
std : : cout < < " Test skipped on Vulkan because it doesn't support multisubpass renderpasses "
< < std : : endl ;
return ;
}
2017-08-04 15:11:26 +00:00
nxt : : BlendState blendState = device . CreateBlendStateBuilder ( )
. SetBlendEnabled ( true )
. SetColorBlend ( nxt : : BlendOperation : : Add , nxt : : BlendFactor : : BlendColor , nxt : : BlendFactor : : One )
. SetAlphaBlend ( nxt : : BlendOperation : : Add , nxt : : BlendFactor : : BlendColor , nxt : : BlendFactor : : One )
. GetResult ( ) ;
2018-02-09 21:51:02 +00:00
nxt : : RenderPass renderpass = device . CreateRenderPassBuilder ( )
2017-08-04 15:11:26 +00:00
. SetAttachmentCount ( 1 )
. SetSubpassCount ( 2 )
. AttachmentSetFormat ( 0 , nxt : : TextureFormat : : R8G8B8A8Unorm )
. SubpassSetColorAttachment ( 0 , 0 , 0 )
. SubpassSetColorAttachment ( 1 , 0 , 0 )
. GetResult ( ) ;
2018-02-09 21:51:02 +00:00
nxt : : Texture renderTarget = device . CreateTextureBuilder ( )
. SetDimension ( nxt : : TextureDimension : : e2D )
. SetExtent ( kRTSize , kRTSize , 1 )
. SetFormat ( nxt : : TextureFormat : : R8G8B8A8Unorm )
. SetMipLevels ( 1 )
. SetAllowedUsage ( nxt : : TextureUsageBit : : OutputAttachment | nxt : : TextureUsageBit : : TransferSrc )
. SetInitialUsage ( nxt : : TextureUsageBit : : OutputAttachment )
. GetResult ( ) ;
nxt : : TextureView renderTargetView = renderTarget . CreateTextureViewBuilder ( ) . GetResult ( ) ;
nxt : : Framebuffer framebuffer = device . CreateFramebufferBuilder ( )
2017-08-04 15:11:26 +00:00
. SetRenderPass ( renderpass )
. SetDimensions ( kRTSize , kRTSize )
. SetAttachment ( 0 , renderTargetView )
. GetResult ( ) ;
nxt : : ShaderModule fsModule = utils : : CreateShaderModule ( device , nxt : : ShaderStage : : Fragment , R " (
# version 450
layout ( set = 0 , binding = 0 ) uniform myBlock {
vec4 color ;
} myUbo ;
layout ( location = 0 ) out vec4 fragColor ;
void main ( ) {
fragColor = myUbo . color ;
}
) " );
basePipeline = device . CreateRenderPipelineBuilder ( )
. SetSubpass ( renderpass , 0 )
. SetLayout ( pipelineLayout )
. SetStage ( nxt : : ShaderStage : : Vertex , vsModule , " main " )
. SetStage ( nxt : : ShaderStage : : Fragment , fsModule , " main " )
. GetResult ( ) ;
testPipeline = device . CreateRenderPipelineBuilder ( )
. SetSubpass ( renderpass , 0 )
. SetLayout ( pipelineLayout )
. SetStage ( nxt : : ShaderStage : : Vertex , vsModule , " main " )
. SetStage ( nxt : : ShaderStage : : Fragment , fsModule , " main " )
. SetColorAttachmentBlendState ( 0 , blendState )
. GetResult ( ) ;
nxt : : RenderPipeline basePipeline2 = device . CreateRenderPipelineBuilder ( )
. SetSubpass ( renderpass , 1 )
. SetLayout ( pipelineLayout )
. SetStage ( nxt : : ShaderStage : : Vertex , vsModule , " main " )
. SetStage ( nxt : : ShaderStage : : Fragment , fsModule , " main " )
. GetResult ( ) ;
nxt : : RenderPipeline testPipeline2 = device . CreateRenderPipelineBuilder ( )
. SetSubpass ( renderpass , 1 )
. SetLayout ( pipelineLayout )
. SetStage ( nxt : : ShaderStage : : Vertex , vsModule , " main " )
. SetStage ( nxt : : ShaderStage : : Fragment , fsModule , " main " )
. SetColorAttachmentBlendState ( 0 , blendState )
. GetResult ( ) ;
// Check that the initial blend color is (0,0,0,0)
{
nxt : : CommandBuffer commands = device . CreateCommandBufferBuilder ( )
. BeginRenderPass ( renderpass , framebuffer )
. BeginRenderSubpass ( )
. SetRenderPipeline ( basePipeline )
. SetBindGroup ( 0 , MakeBindGroupForColors ( std : : array < RGBA8 , 1 > ( { { RGBA8 ( 0 , 0 , 0 , 0 ) } } ) ) )
. DrawArrays ( 3 , 1 , 0 , 0 )
. SetRenderPipeline ( testPipeline )
. SetBindGroup ( 0 , MakeBindGroupForColors ( std : : array < RGBA8 , 1 > ( { { RGBA8 ( 255 , 255 , 255 , 255 ) } } ) ) )
. DrawArrays ( 3 , 1 , 0 , 0 )
. EndRenderSubpass ( )
. EndRenderPass ( )
. GetResult ( ) ;
queue . Submit ( 1 , & commands ) ;
EXPECT_PIXEL_RGBA8_EQ ( RGBA8 ( 0 , 0 , 0 , 0 ) , renderTarget , kRTSize / 2 , kRTSize / 2 ) ;
}
// Check that setting the blend color works
{
nxt : : CommandBuffer commands = device . CreateCommandBufferBuilder ( )
. BeginRenderPass ( renderpass , framebuffer )
. BeginRenderSubpass ( )
. SetRenderPipeline ( basePipeline )
. SetBindGroup ( 0 , MakeBindGroupForColors ( std : : array < RGBA8 , 1 > ( { { RGBA8 ( 0 , 0 , 0 , 0 ) } } ) ) )
. DrawArrays ( 3 , 1 , 0 , 0 )
. SetRenderPipeline ( testPipeline )
. SetBlendColor ( 1 , 1 , 1 , 1 )
. SetBindGroup ( 0 , MakeBindGroupForColors ( std : : array < RGBA8 , 1 > ( { { RGBA8 ( 255 , 255 , 255 , 255 ) } } ) ) )
. DrawArrays ( 3 , 1 , 0 , 0 )
. EndRenderSubpass ( )
. EndRenderPass ( )
. GetResult ( ) ;
queue . Submit ( 1 , & commands ) ;
EXPECT_PIXEL_RGBA8_EQ ( RGBA8 ( 255 , 255 , 255 , 255 ) , renderTarget , kRTSize / 2 , kRTSize / 2 ) ;
}
// Check that the blend color is not inherited
{
nxt : : CommandBuffer commands = device . CreateCommandBufferBuilder ( )
. BeginRenderPass ( renderpass , framebuffer )
. BeginRenderSubpass ( )
. SetRenderPipeline ( basePipeline )
. SetBindGroup ( 0 , MakeBindGroupForColors ( std : : array < RGBA8 , 1 > ( { { RGBA8 ( 0 , 0 , 0 , 0 ) } } ) ) )
. DrawArrays ( 3 , 1 , 0 , 0 )
. SetRenderPipeline ( testPipeline )
. SetBlendColor ( 1 , 1 , 1 , 1 )
. SetBindGroup ( 0 , MakeBindGroupForColors ( std : : array < RGBA8 , 1 > ( { { RGBA8 ( 255 , 255 , 255 , 255 ) } } ) ) )
. DrawArrays ( 3 , 1 , 0 , 0 )
. EndRenderSubpass ( )
. BeginRenderSubpass ( )
. SetRenderPipeline ( basePipeline2 )
. SetBindGroup ( 0 , MakeBindGroupForColors ( std : : array < RGBA8 , 1 > ( { { RGBA8 ( 0 , 0 , 0 , 0 ) } } ) ) )
. DrawArrays ( 3 , 1 , 0 , 0 )
. SetRenderPipeline ( testPipeline2 )
. SetBindGroup ( 0 , MakeBindGroupForColors ( std : : array < RGBA8 , 1 > ( { { RGBA8 ( 255 , 255 , 255 , 255 ) } } ) ) )
. DrawArrays ( 3 , 1 , 0 , 0 )
. EndRenderSubpass ( )
. EndRenderPass ( )
. GetResult ( ) ;
queue . Submit ( 1 , & commands ) ;
EXPECT_PIXEL_RGBA8_EQ ( RGBA8 ( 0 , 0 , 0 , 0 ) , renderTarget , kRTSize / 2 , kRTSize / 2 ) ;
}
}
2018-02-02 22:32:28 +00:00
NXT_INSTANTIATE_TEST ( BlendStateTest , D3D12Backend , MetalBackend , OpenGLBackend , VulkanBackend )