0513 cpp 비트필드

260513

C/C++ 비트필드 문법

C에서부터 이어져 온

기괴한 문법이다.

대충

short a : 1

이렇게 쓰면 a는 16비트 중에서 1비트만 쓴다.

다시 말해서

boolean을 1바이트가 아닌

1비트로 저장하게 해준다.

tight-packing인것이다.

웃긴 건

밑에 벌칸 코드에서

unsigned int cullMode : 2

이게 뭘 뜻하는지 알겠는가?

맞다.

GL과 똑같이,

None / Front / Back / FrontAndBack

이렇게 4개밖에 존재하지 않기 때문에,

VK_CULL_MODE_NONE = 0
VK_CULL_MODE_FRONT_BIT = 0x1
VK_CULL_MODE_BACK_BIT = 0x2
VK_CULL_MODE_FRONT_AND_BACK = 0x3

걍 2비트, 00 / 01 / 10 / 11 로

저장하는 것이다.

또 VkBlendFactor같이

// Provided by VK_VERSION_1_0
typedef enum VkBlendFactor {
    VK_BLEND_FACTOR_ZERO = 0,
    VK_BLEND_FACTOR_ONE = 1,
    VK_BLEND_FACTOR_SRC_COLOR = 2,
    VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR = 3,
    VK_BLEND_FACTOR_DST_COLOR = 4,
    VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR = 5,
    VK_BLEND_FACTOR_SRC_ALPHA = 6,
    VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA = 7,
    VK_BLEND_FACTOR_DST_ALPHA = 8,
    VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA = 9,
    VK_BLEND_FACTOR_CONSTANT_COLOR = 10,
    VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR = 11,
    VK_BLEND_FACTOR_CONSTANT_ALPHA = 12,
    VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA = 13,
    VK_BLEND_FACTOR_SRC_ALPHA_SATURATE = 14,
    VK_BLEND_FACTOR_SRC1_COLOR = 15,
    VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR = 16,
    VK_BLEND_FACTOR_SRC1_ALPHA = 17,
    VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA = 18,
} VkBlendFactor;

이렇게 애매...하게 19개 있는 경우?

    unsigned int srcColor : 5;  // VkBlendFactor
    unsigned int destColor : 5;  // VkBlendFactor
    unsigned int srcAlpha : 5;  // VkBlendFactor
    unsigned int destAlpha : 5;  // VkBlendFactor

이렇게 5비트를 써서?

2의 5승 마이너스 1. 최대 0 ~ 31까지 표현이 가능하게 한다.


밑에 코드는

Blend가 36비트,

DepthStencil관련이 22비트,

레스터라이저가 6비트

36+22+6 = 64비트 딱 나온다.

근데 이런거 주의할 게

컴파일러마다 다르단다.

하드웨어 플랫폼에 따라서도 다를 수 있단다.

따라서 컴파일 타임 어설션을 넣어주자.

static_assert(sizeof(VulkanPipelineRasterStateKey) == 8);

참고로 쓰는 비트 수가

타입 크기를 전부 더한것보다 큰 경우

걍 잘린다고 한다.


(이게 아마 psp에뮬에서 뽑아왔던걸로 기억한다..)

// Let's pack this tight using bitfields.
// If an enable flag is set to 0, all the data fields for that section should
// also be set to 0.
// ~64 bits.
// Can't use enums unfortunately, they end up signed and breaking values above half their ranges.
struct VulkanPipelineRasterStateKey {
    // Blend
    unsigned int blendEnable : 1;
    unsigned int srcColor : 5;  // VkBlendFactor
    unsigned int destColor : 5;  // VkBlendFactor
    unsigned int srcAlpha : 5;  // VkBlendFactor
    unsigned int destAlpha : 5;  // VkBlendFactor
    // bool useBlendConstant : 1;  // sacrifice a bit to cheaply check if we need to update the blend color
    unsigned int blendOpColor : 3;  // VkBlendOp
    unsigned int blendOpAlpha : 3;  // VkBlendOp
    unsigned int logicOpEnable : 1;
    unsigned int logicOp : 4;  // VkLogicOp
    unsigned int colorWriteMask : 4;

    // Depth/Stencil
    unsigned int depthClampEnable : 1;
    unsigned int depthTestEnable : 1;
    unsigned int depthWriteEnable : 1;
    unsigned int depthCompareOp : 3;  // VkCompareOp 
    unsigned int stencilTestEnable : 1;
    unsigned int stencilCompareOp : 3;  // VkCompareOp
    unsigned int stencilPassOp : 4; // VkStencilOp
    unsigned int stencilFailOp : 4; // VkStencilOp 
    unsigned int stencilDepthFailOp : 4;  // VkStencilOp 

    // We'll use dynamic state for writemask, reference and comparemask to start with,
    // and viewport/scissor.

    // Rasterizer
    unsigned int cullMode : 2;  // VkCullModeFlagBits 
    unsigned int topology : 4;  // VkPrimitiveTopology 

    bool operator < (const VulkanPipelineRasterStateKey &other) const {
        size_t size = sizeof(VulkanPipelineRasterStateKey);
        return memcmp(this, &other, size) < 0;
    }
};