Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support to BC4 and BC5 format #2013

Merged
merged 1 commit into from Jun 24, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions core/image/BUILD.bazel
Expand Up @@ -31,6 +31,7 @@ go_library(
"png.go",
"resizer.go",
"rgba_f32.go",
"rgtc.go",
"s3.go",
"s3_dxt1_rgb.go",
"s3_dxt1_rgba.go",
Expand Down
4 changes: 4 additions & 0 deletions core/image/decompress_test.go
Expand Up @@ -180,6 +180,10 @@ func TestDecompressors(t *testing.T) {
{image.S3_DXT3_RGBA, ".bin"},
{image.S3_DXT5_RGBA, ".bin"},
{astc.RGBA_4x4, ".astc"},
{image.RGTC1_BC4_R_U8_NORM, ".bin"},
{image.RGTC1_BC4_R_S8_NORM, ".bin"},
{image.RGTC2_BC5_RG_U8_NORM, ".bin"},
{image.RGTC2_BC5_RG_S8_NORM, ".bin"},
} {
name := test.fmt.Name
inPath := filepath.Join("test_data", name+test.ext)
Expand Down
14 changes: 13 additions & 1 deletion core/image/image.proto
Expand Up @@ -75,6 +75,10 @@ message Format {
FmtS3_DXT3_RGBA s3_dxt3_rgba = 17;
FmtS3_DXT5_RGBA s3_dxt5_rgba = 18;
FmtASTC astc = 19;
FmtRGTC1_BC4_R_U8_NORM rgtc1_bc4_r_u8_norm = 20;
FmtRGTC1_BC4_R_S8_NORM rgtc1_bc4_r_s8_norm = 21;
FmtRGTC2_BC5_RG_U8_NORM rgtc2_bc5_rg_u8_norm = 22;
FmtRGTC2_BC5_RG_S8_NORM rgtc2_bc5_rg_s8_norm = 23;
}
}

Expand Down Expand Up @@ -121,6 +125,14 @@ message FmtASTC {
uint32 block_height = 2;
bool srgb = 3;
}
message FmtRGTC1_BC4_R_U8_NORM {
}
message FmtRGTC1_BC4_R_S8_NORM {
}
message FmtRGTC2_BC5_RG_U8_NORM {
}
message FmtRGTC2_BC5_RG_S8_NORM {
}

// GAPIS internal structure.
message ConvertResolvable {
Expand Down Expand Up @@ -149,4 +161,4 @@ message ResizeResolvable {
uint32 dst_width = 6;
uint32 dst_height = 7;
uint32 dst_depth = 8;
}
}
210 changes: 210 additions & 0 deletions core/image/rgtc.go
@@ -0,0 +1,210 @@
// Copyright (C) 2018 Google Inc.
//
// 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.

package image

import (
"github.com/google/gapid/core/data/binary"
"github.com/google/gapid/core/math/sint"
"github.com/google/gapid/core/stream"
)

var (
RGTC1_BC4_R_U8_NORM = NewRGTC1_BC4_R_U8_NORM("RGTC1_BC4_R_U8_NORM")
RGTC1_BC4_R_S8_NORM = NewRGTC1_BC4_R_S8_NORM("RGTC1_BC4_R_S8_NORM")
RGTC2_BC5_RG_U8_NORM = NewRGTC2_BC5_RG_U8_NORM("RGTC2_BC5_RG_U8_NORM")
RGTC2_BC5_RG_S8_NORM = NewRGTC2_BC5_RG_S8_NORM("RGTC2_BC5_RG_S8_NORM")
)

func init() {
RegisterConverter(RGTC1_BC4_R_U8_NORM, RGBA_U8_NORM, func(src []byte, w, h, d int) ([]byte, error) {
return decode4x4Blocks(src, w, h, d, func(r binary.Reader, dst []pixel) {
decodeRGTC(r, dst, false, false)
})
})
RegisterConverter(RGTC1_BC4_R_S8_NORM, RGBA_U8_NORM, func(src []byte, w, h, d int) ([]byte, error) {
return decode4x4Blocks(src, w, h, d, func(r binary.Reader, dst []pixel) {
decodeRGTC(r, dst, true, false)
})
})
RegisterConverter(RGTC2_BC5_RG_U8_NORM, RGBA_U8_NORM, func(src []byte, w, h, d int) ([]byte, error) {
return decode4x4Blocks(src, w, h, d, func(r binary.Reader, dst []pixel) {
decodeRGTC(r, dst, false, true)
})
})
RegisterConverter(RGTC2_BC5_RG_S8_NORM, RGBA_U8_NORM, func(src []byte, w, h, d int) ([]byte, error) {
return decode4x4Blocks(src, w, h, d, func(r binary.Reader, dst []pixel) {
decodeRGTC(r, dst, true, true)
})
})
}

// NewRGTC1_BC4_R_U8_NORM returns a format representing the RGTC1_BC4_R_U8_NORM
// block texture compression.
func NewRGTC1_BC4_R_U8_NORM(name string) *Format {
return &Format{name, &Format_Rgtc1Bc4RU8Norm{&FmtRGTC1_BC4_R_U8_NORM{}}}
}

func (f *FmtRGTC1_BC4_R_U8_NORM) key() interface{} {
return *f
}
func (*FmtRGTC1_BC4_R_U8_NORM) size(w, h, d int) int {
return d * (sint.Max(sint.AlignUp(w, 4), 4) * sint.Max(sint.AlignUp(h, 4), 4)) / 2
}
func (f *FmtRGTC1_BC4_R_U8_NORM) check(data []byte, w, h, d int) error {
return checkSize(data, f, w, h, d)
}
func (*FmtRGTC1_BC4_R_U8_NORM) channels() stream.Channels {
return stream.Channels{stream.Channel_Red}
}

// NewRGTC1_BC4_R_S8_NORM returns a format representing the RGTC1_BC4_R_S8_NORM
// block texture compression.
func NewRGTC1_BC4_R_S8_NORM(name string) *Format {
return &Format{name, &Format_Rgtc1Bc4RS8Norm{&FmtRGTC1_BC4_R_S8_NORM{}}}
}

func (f *FmtRGTC1_BC4_R_S8_NORM) key() interface{} {
return *f
}
func (*FmtRGTC1_BC4_R_S8_NORM) size(w, h, d int) int {
return d * (sint.Max(sint.AlignUp(w, 4), 4) * sint.Max(sint.AlignUp(h, 4), 4)) / 2
}
func (f *FmtRGTC1_BC4_R_S8_NORM) check(data []byte, w, h, d int) error {
return checkSize(data, f, w, h, d)
}
func (*FmtRGTC1_BC4_R_S8_NORM) channels() stream.Channels {
return stream.Channels{stream.Channel_Red}
}

// NewRGTC2_BC5_RG_U8_NORM returns a format representing the RGTC2_BC5_RG_U8_NORM
// block texture compression.
func NewRGTC2_BC5_RG_U8_NORM(name string) *Format {
return &Format{name, &Format_Rgtc2Bc5RgU8Norm{&FmtRGTC2_BC5_RG_U8_NORM{}}}
}

func (f *FmtRGTC2_BC5_RG_U8_NORM) key() interface{} {
return *f
}
func (*FmtRGTC2_BC5_RG_U8_NORM) size(w, h, d int) int {
return d * (sint.Max(sint.AlignUp(w, 4), 4) * sint.Max(sint.AlignUp(h, 4), 4))
}
func (f *FmtRGTC2_BC5_RG_U8_NORM) check(data []byte, w, h, d int) error {
return checkSize(data, f, w, h, d)
}
func (*FmtRGTC2_BC5_RG_U8_NORM) channels() stream.Channels {
return stream.Channels{stream.Channel_Red, stream.Channel_Green}
}

// NewRGTC2_BC5_RG_S8_NORM returns a format representing the RGTC2_BC5_RG_S8_NORM
// block texture compression.
func NewRGTC2_BC5_RG_S8_NORM(name string) *Format {
return &Format{name, &Format_Rgtc2Bc5RgS8Norm{&FmtRGTC2_BC5_RG_S8_NORM{}}}
}

func (f *FmtRGTC2_BC5_RG_S8_NORM) key() interface{} {
return *f
}
func (*FmtRGTC2_BC5_RG_S8_NORM) size(w, h, d int) int {
return d * (sint.Max(sint.AlignUp(w, 4), 4) * sint.Max(sint.AlignUp(h, 4), 4))
}
func (f *FmtRGTC2_BC5_RG_S8_NORM) check(data []byte, w, h, d int) error {
return checkSize(data, f, w, h, d)
}
func (*FmtRGTC2_BC5_RG_S8_NORM) channels() stream.Channels {
return stream.Channels{stream.Channel_Red, stream.Channel_Green}
}

func decodeRGTC(r binary.Reader, dst []pixel, signed bool, hasGreen bool) {
r0, r1, rc := r.Uint8(), r.Uint8(), uint64(r.Uint16())|(uint64(r.Uint32())<<16)
mixer := &rgtcMixer{
signed: signed,
r0: r0,
r1: r1,
rc: rc,
}
if hasGreen {
g0, g1, gc := r.Uint8(), r.Uint8(), uint64(r.Uint16())|(uint64(r.Uint32())<<16)
mixer.hasGreen, mixer.g0, mixer.g1, mixer.gc = hasGreen, g0, g1, gc
}
for i := 0; i < 16; i++ {
dst[i].r = mixer.red(i)
dst[i].g = mixer.green(i)
dst[i].b = 0
dst[i].a = 255
}
}

type rgtcMixer struct {
signed, hasGreen bool
r0, r1 uint8
g0, g1 uint8
rc, gc uint64
}

func (m *rgtcMixer) red(pos int) int {
return m.mix(m.r0, m.r1, m.rc, pos)
}

func (m *rgtcMixer) green(pos int) int {
if !m.hasGreen {
return 0
}
return m.mix(m.g0, m.g1, m.gc, pos)
}

func (m *rgtcMixer) mix(v0, v1 uint8, codes uint64, pos int) int {
codes = codes >> uint(pos*3)
if !m.signed {
return mixAlphaDXT5(int(v0), int(v1), codes)
}
return mixRGTCSigned(int8(v0), int8(v1), codes)
}

func mixRGTCSigned(v0, v1 int8, code uint64) int {
toFloat := func(i int8) float32 {
if int(i) > -128 {
return float32(int(i)) / 127.0
}
return -1.0
}
f0 := toFloat(v0)
f1 := toFloat(v1)
var f float32
c := code & 0x7
if f0 > f1 {
switch c {
case 0:
f = f0
case 1:
f = f1
default:
f = (f0*float32(8-c) + f1*float32(c-1)) / 7.0
}
} else {
switch c {
case 0:
f = f0
case 1:
f = f1
case 6:
f = -1.0
case 7:
f = 1.0
default:
f = (f0*float32(6-c) + f1*float32(c-1)) / 5.0
}
}
return int((f + 1.0) * 255.0 / 2.0)
}
51 changes: 25 additions & 26 deletions core/image/s3.go
Expand Up @@ -160,36 +160,35 @@ func decodeAlphaDXT3(r binary.Reader, dst []pixel) {

func decodeAlphaDXT5(r binary.Reader, dst []pixel) {
a0, a1, codes := int(r.Uint8()), int(r.Uint8()), uint64(r.Uint16())|(uint64(r.Uint32())<<16)
for i := 0; i < 16; i++ {
dst[i].a = mixAlphaDXT5(a0, a1, codes)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ignorable: I unfortunately suspect this might not inline, but I do side with readability / reuse than raw performance in this case.

codes >>= 3
}
}

func mixAlphaDXT5(a0, a1 int, code uint64) int {
c := int(code & 0x7)
if a0 > a1 {
for i := 0; i < 16; i++ {
c := int(codes & 0x7)
switch c {
case 0:
dst[i].a = a0
case 1:
dst[i].a = a1
default:
dst[i].a = (a0*(8-c) + a1*(c-1)) / 7
}
codes >>= 3
switch c {
case 0:
return a0
case 1:
return a1
default:
return (a0*(8-c) + a1*(c-1)) / 7
}
} else {
for i := 0; i < 16; i++ {
c := int(codes & 0x7)
switch {
case c == 0:
dst[i].a = a0
case c == 1:
dst[i].a = a1
case c >= 2 && c <= 5:
dst[i].a = (a0*(6-c) + a1*(c-1)) / 5
case c == 6:
dst[i].a = 0
case c == 7:
dst[i].a = 255
}
codes >>= 3
switch c {
case 0:
return a0
case 1:
return a1
case 6:
return 0
case 7:
return 255
default:
return (a0*(6-c) + a1*(c-1)) / 5
}
}
}
Expand Down
Binary file added core/image/test_data/RGTC1_BC4_R_S8_NORM.bin
Binary file not shown.
Binary file added core/image/test_data/RGTC1_BC4_R_S8_NORM.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added core/image/test_data/RGTC1_BC4_R_U8_NORM.bin
Binary file not shown.
Binary file added core/image/test_data/RGTC1_BC4_R_U8_NORM.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added core/image/test_data/RGTC2_BC5_RG_S8_NORM.bin
Binary file not shown.
Binary file added core/image/test_data/RGTC2_BC5_RG_S8_NORM.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added core/image/test_data/RGTC2_BC5_RG_U8_NORM.bin
Binary file not shown.
Binary file added core/image/test_data/RGTC2_BC5_RG_U8_NORM.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 4 additions & 4 deletions gapis/api/vulkan/resources.go
Expand Up @@ -343,13 +343,13 @@ func getImageFormatFromVulkanFormat(vkfmt VkFormat) (*image.Format, error) {
case VkFormat_VK_FORMAT_BC3_SRGB_BLOCK:
return image.NewS3_DXT5_RGBA("VK_FORMAT_BC3_SRGB_BLOCK"), nil
case VkFormat_VK_FORMAT_BC4_UNORM_BLOCK:
return nil, &unsupportedVulkanFormatError{Format: vkfmt}
return image.NewRGTC1_BC4_R_U8_NORM("VK_FORMAT_BC4_UNORM_BLOCK"), nil
case VkFormat_VK_FORMAT_BC4_SNORM_BLOCK:
return nil, &unsupportedVulkanFormatError{Format: vkfmt}
return image.NewRGTC1_BC4_R_S8_NORM("VK_FORMAT_BC4_SNORM_BLOCK"), nil
case VkFormat_VK_FORMAT_BC5_UNORM_BLOCK:
return nil, &unsupportedVulkanFormatError{Format: vkfmt}
return image.NewRGTC2_BC5_RG_U8_NORM("VK_FORMAT_BC5_UNORM_BLOCK"), nil
case VkFormat_VK_FORMAT_BC5_SNORM_BLOCK:
return nil, &unsupportedVulkanFormatError{Format: vkfmt}
return image.NewRGTC2_BC5_RG_S8_NORM("VK_FORMAT_BC5_SNORM_BLOCK"), nil
case VkFormat_VK_FORMAT_BC6H_UFLOAT_BLOCK:
return nil, &unsupportedVulkanFormatError{Format: vkfmt}
case VkFormat_VK_FORMAT_BC6H_SFLOAT_BLOCK:
Expand Down