浏览代码

(wip) Added BC6H fast encode

/Add-support-for-light-specular-color-tint
Frédéric Vauchelles 7 年前
当前提交
2fb41866
共有 16 个文件被更改,包括 1549 次插入0 次删除
  1. 57
      ScriptableRenderPipeline/Core/BC6H.cs
  2. 13
      ScriptableRenderPipeline/Core/BC6H.cs.meta
  3. 45
      ScriptableRenderPipeline/Core/Resources/BC6H.compute
  4. 10
      ScriptableRenderPipeline/Core/Resources/BC6H.compute.meta
  5. 188
      ScriptableRenderPipeline/Core/ShaderLibrary/BC6H.hlsl
  6. 10
      ScriptableRenderPipeline/Core/ShaderLibrary/BC6H.hlsl.meta
  7. 10
      Tests/TextureCompressionTests.meta
  8. 10
      Tests/TextureCompressionTests/Editor.meta
  9. 65
      Tests/TextureCompressionTests/Editor/BC6HTests.cs
  10. 13
      Tests/TextureCompressionTests/Editor/BC6HTests.cs.meta
  11. 10
      Tests/TextureCompressionTests/Editor/Resources.meta
  12. 1001
      Tests/TextureCompressionTests/Editor/Resources/graffiti_shelter_1k.hdr
  13. 117
      Tests/TextureCompressionTests/Editor/Resources/graffiti_shelter_1k.hdr.meta

57
ScriptableRenderPipeline/Core/BC6H.cs


using UnityEngine.Assertions;
using UnityEngine.Rendering;
namespace UnityEngine.Experimental.Rendering
{
public class BC6H
{
static readonly int _Source = Shader.PropertyToID("_Source");
static readonly int _Target = Shader.PropertyToID("_Target");
readonly ComputeShader m_Shader;
readonly int m_KernelEncodeFast;
readonly int[] m_KernelEncodeFastGroupSize;
public BC6H(ComputeShader shader)
{
Assert.IsNotNull(shader);
m_Shader = shader;
m_KernelEncodeFast = m_Shader.FindKernel("KEncodeFast");
uint x, y, z;
m_Shader.GetKernelThreadGroupSizes(m_KernelEncodeFast, out x, out y, out z);
m_KernelEncodeFastGroupSize = new[] { (int)x, (int)y, (int)z };
}
public RenderTexture InstantiateTarget(int sourceWidth, int sourceHeight)
{
int targetWidth, targetHeight;
CalculateOutputSize(sourceWidth, sourceHeight, out targetWidth, out targetHeight);
var t = new RenderTexture(targetWidth, targetHeight, 0, RenderTextureFormat.ARGBFloat, RenderTextureReadWrite.Linear);
t.Release();
t.enableRandomWrite = true;
t.Create();
return t;
}
// Only use mode11 of BC6H encoding
public void EncodeFast(CommandBuffer cmb, RenderTargetIdentifier source, int sourceWidth, int sourceHeight, RenderTargetIdentifier target)
{
int targetWidth, targetHeight;
CalculateOutputSize(sourceWidth, sourceHeight, out targetWidth, out targetHeight);
cmb.SetComputeTextureParam(m_Shader, m_KernelEncodeFast, _Source, source);
cmb.SetComputeTextureParam(m_Shader, m_KernelEncodeFast, _Target, target);
cmb.DispatchCompute(m_Shader, m_KernelEncodeFast, targetWidth / m_KernelEncodeFastGroupSize[0], targetHeight / m_KernelEncodeFastGroupSize[1], 1);
}
static void CalculateOutputSize(int swidth, int sheight, out int twidth, out int theight)
{
// BC6H encode 4x4 blocks of 32bit in 128bit
twidth = swidth >> 2;
theight = sheight >> 2;
}
}
}

13
ScriptableRenderPipeline/Core/BC6H.cs.meta


fileFormatVersion: 2
guid: ef7e375d470b6404a9e355690703502b
timeCreated: 1507290672
licenseType: Pro
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

45
ScriptableRenderPipeline/Core/Resources/BC6H.compute


#include "../ShaderLibrary/BC6H.hlsl"
Texture2D<float4> _Source;
RWTexture2D<float4> _Target;
#pragma kernel KEncodeFast
[numthreads(4, 4, 1)]
void KEncodeFast(uint2 groupId : SV_GroupID, uint2 groupThreadId : SV_GroupThreadID, uint2 dispatchThreadId : SV_DispatchThreadID)
{
// Load 4x4 pixel block
float3 texels[16];
uint2 topLeftSourceID = dispatchThreadId << 2;
texels[0] = _Source.Load(uint3(topLeftSourceID , 0)).rgb;
texels[1] = _Source.Load(uint3(topLeftSourceID + uint2(1, 0), 0)).rgb;
texels[2] = _Source.Load(uint3(topLeftSourceID + uint2(2, 0), 0)).rgb;
texels[3] = _Source.Load(uint3(topLeftSourceID + uint2(3, 0), 0)).rgb;
texels[4] = _Source.Load(uint3(topLeftSourceID + uint2(0, 1), 0)).rgb;
texels[5] = _Source.Load(uint3(topLeftSourceID + uint2(1, 1), 0)).rgb;
texels[6] = _Source.Load(uint3(topLeftSourceID + uint2(2, 1), 0)).rgb;
texels[7] = _Source.Load(uint3(topLeftSourceID + uint2(3, 1), 0)).rgb;
texels[8] = _Source.Load(uint3(topLeftSourceID + uint2(0, 2), 0)).rgb;
texels[9] = _Source.Load(uint3(topLeftSourceID + uint2(1, 2), 0)).rgb;
texels[10] = _Source.Load(uint3(topLeftSourceID + uint2(2, 2), 0)).rgb;
texels[11] = _Source.Load(uint3(topLeftSourceID + uint2(3, 2), 0)).rgb;
texels[12] = _Source.Load(uint3(topLeftSourceID + uint2(0, 3), 0)).rgb;
texels[13] = _Source.Load(uint3(topLeftSourceID + uint2(1, 3), 0)).rgb;
texels[14] = _Source.Load(uint3(topLeftSourceID + uint2(2, 3), 0)).rgb;
texels[15] = _Source.Load(uint3(topLeftSourceID + uint2(3, 3), 0)).rgb;
uint4 block = uint4(0, 0, 0, 0);
float blockMSLE = 0;
EncodeMode11(block, blockMSLE, texels);
_Target[dispatchThreadId] = float4(0, 0, 0, 0);
}
#pragma kernel KCopy
[numthreads(1, 1, 1)]
void KCopy(uint2 groupId : SV_GroupID, uint2 groupThreadId : SV_GroupThreadID, uint2 dispatchThreadId : SV_DispatchThreadID)
{
_Target[dispatchThreadId] = _Source.Load(uint3(dispatchThreadId, 0));
}

10
ScriptableRenderPipeline/Core/Resources/BC6H.compute.meta


fileFormatVersion: 2
guid: b69b95b3420fd904e8530b79f665a1f8
timeCreated: 1507123133
licenseType: Pro
ComputeShaderImporter:
externalObjects: {}
currentAPIMask: 4
userData:
assetBundleName:
assetBundleVariant:

188
ScriptableRenderPipeline/Core/ShaderLibrary/BC6H.hlsl


// Ref: https://github.com/knarkowicz/GPURealTimeBC6H/blob/master/bin/compress.hlsl
// Doc: https://msdn.microsoft.com/en-us/library/windows/desktop/hh308952(v=vs.85).aspx
// Measure compression error
float CalcMSLE(float3 a, float3 b)
{
float3 err = log2(( b + 1.0f) / (a + 1.0f ));
err = err * err;
return err.x + err.y + err.z;
}
// Quantification Helpers
float3 Quantize7(float3 x)
{
return (f32tof16(x) * 128.0f) / (0x7bff + 1.0f);
}
float3 Quantize9(float3 x)
{
return (f32tof16(x) * 512.0f) / (0x7bff + 1.0f);
}
float3 Quantize10(float3 x)
{
return (f32tof16(x) * 1024.0f) / (0x7bff + 1.0f);
}
float3 Unquantize7(float3 x)
{
return (x * 65536.0f + 0x8000) / 128.0f;
}
float3 Unquantize9(float3 x)
{
return (x * 65536.0f + 0x8000) / 512.0f;
}
float3 Unquantize10(float3 x)
{
return (x * 65536.0f + 0x8000) / 1024.0f;
}
// Swap helpers
void Swap(inout float3 a, inout float3 b)
{
float3 tmp = a;
a = b;
b = tmp;
}
void Swap(inout float a, inout float b)
{
float tmp = a;
a = b;
b = tmp;
}
// BC6H Helpers
// Compute index of a texel projected against endpoints
uint ComputeIndex3( float texelPos, float endPoint0Pos, float endPoint1Pos )
{
float r = ( texelPos - endPoint0Pos ) / ( endPoint1Pos - endPoint0Pos );
return (uint) clamp( r * 6.98182f + 0.00909f + 0.5f, 0.0f, 7.0f );
}
uint ComputeIndex4( float texelPos, float endPoint0Pos, float endPoint1Pos )
{
float r = ( texelPos - endPoint0Pos ) / ( endPoint1Pos - endPoint0Pos );
return (uint) clamp( r * 14.93333f + 0.03333f + 0.5f, 0.0f, 15.0f );
}
void SignExtend( inout float3 v1, uint mask, uint signFlag )
{
int3 v = (int3) v1;
v.x = ( v.x & mask ) | ( v.x < 0 ? signFlag : 0 );
v.y = ( v.y & mask ) | ( v.y < 0 ? signFlag : 0 );
v.z = ( v.z & mask ) | ( v.z < 0 ? signFlag : 0 );
v1 = v;
}
// 2nd step for unquantize
float3 FinishUnquantize( float3 endpoint0Unq, float3 endpoint1Unq, float weight )
{
float3 comp = ( endpoint0Unq * ( 64.0f - weight ) + endpoint1Unq * weight + 32.0f ) * ( 31.0f / 4096.0f );
return f16tof32( uint3( comp ) );
}
// BC6H Modes
void EncodeMode11( inout uint4 block, inout float blockMSLE, float3 texels[ 16 ] )
{
// compute endpoints (min/max RGB bbox)
float3 blockMin = texels[ 0 ];
float3 blockMax = texels[ 0 ];
for ( uint i = 1; i < 16; ++i )
{
blockMin = min( blockMin, texels[ i ] );
blockMax = max( blockMax, texels[ i ] );
}
// refine endpoints in log2 RGB space
float3 refinedBlockMin = blockMax;
float3 refinedBlockMax = blockMin;
for ( uint i = 0; i < 16; ++i )
{
refinedBlockMin = min( refinedBlockMin, texels[ i ] == blockMin ? refinedBlockMin : texels[ i ] );
refinedBlockMax = max( refinedBlockMax, texels[ i ] == blockMax ? refinedBlockMax : texels[ i ] );
}
float3 logBlockMax = log2( blockMax + 1.0f );
float3 logBlockMin = log2( blockMin + 1.0f );
float3 logRefinedBlockMax = log2( refinedBlockMax + 1.0f );
float3 logRefinedBlockMin = log2( refinedBlockMin + 1.0f );
float3 logBlockMaxExt = ( logBlockMax - logBlockMin ) * ( 1.0f / 32.0f );
logBlockMin += min( logRefinedBlockMin - logBlockMin, logBlockMaxExt );
logBlockMax -= min( logBlockMax - logRefinedBlockMax, logBlockMaxExt );
blockMin = exp2( logBlockMin ) - 1.0f;
blockMax = exp2( logBlockMax ) - 1.0f;
float3 blockDir = blockMax - blockMin;
blockDir = blockDir / ( blockDir.x + blockDir.y + blockDir.z );
float3 endpoint0 = Quantize10( blockMin );
float3 endpoint1 = Quantize10( blockMax );
float endPoint0Pos = f32tof16( dot( blockMin, blockDir ) );
float endPoint1Pos = f32tof16( dot( blockMax, blockDir ) );
// check if endpoint swap is required
float fixupTexelPos = f32tof16( dot( texels[ 0 ], blockDir ) );
uint fixupIndex = ComputeIndex4( fixupTexelPos, endPoint0Pos, endPoint1Pos );
if ( fixupIndex > 7 )
{
Swap( endPoint0Pos, endPoint1Pos );
Swap( endpoint0, endpoint1 );
}
// compute indices
uint indices[ 16 ] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
for ( uint i = 0; i < 16; ++i )
{
float texelPos = f32tof16( dot( texels[ i ], blockDir ) );
indices[ i ] = ComputeIndex4( texelPos, endPoint0Pos, endPoint1Pos );
}
// compute compression error (MSLE)
float3 endpoint0Unq = Unquantize10( endpoint0 );
float3 endpoint1Unq = Unquantize10( endpoint1 );
float msle = 0.0f;
for ( uint i = 0; i < 16; ++i )
{
float weight = floor( ( indices[ i ] * 64.0f ) / 15.0f + 0.5f );
float3 texelUnc = FinishUnquantize( endpoint0Unq, endpoint1Unq, weight );
msle += CalcMSLE( texels[ i ], texelUnc );
}
// encode block for mode 11
blockMSLE = msle;
block.x = 0x03;
// endpoints
block.x |= (uint) endpoint0.x << 5;
block.x |= (uint) endpoint0.y << 15;
block.x |= (uint) endpoint0.z << 25;
block.y |= (uint) endpoint0.z >> 7;
block.y |= (uint) endpoint1.x << 3;
block.y |= (uint) endpoint1.y << 13;
block.y |= (uint) endpoint1.z << 23;
block.z |= (uint) endpoint1.z >> 9;
// indices
block.z |= indices[ 0 ] << 1;
block.z |= indices[ 1 ] << 4;
block.z |= indices[ 2 ] << 8;
block.z |= indices[ 3 ] << 12;
block.z |= indices[ 4 ] << 16;
block.z |= indices[ 5 ] << 20;
block.z |= indices[ 6 ] << 24;
block.z |= indices[ 7 ] << 28;
block.w |= indices[ 8 ] << 0;
block.w |= indices[ 9 ] << 4;
block.w |= indices[ 10 ] << 8;
block.w |= indices[ 11 ] << 12;
block.w |= indices[ 12 ] << 16;
block.w |= indices[ 13 ] << 20;
block.w |= indices[ 14 ] << 24;
block.w |= indices[ 15 ] << 28;
}

10
ScriptableRenderPipeline/Core/ShaderLibrary/BC6H.hlsl.meta


fileFormatVersion: 2
guid: 27d419a4917d0ea49978c236e058d464
timeCreated: 1507282342
licenseType: Pro
ShaderImporter:
externalObjects: {}
defaultTextures: []
userData:
assetBundleName:
assetBundleVariant:

10
Tests/TextureCompressionTests.meta


fileFormatVersion: 2
guid: 7dc9213dd6e16494ab2ad39569001c48
folderAsset: yes
timeCreated: 1507290598
licenseType: Pro
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

10
Tests/TextureCompressionTests/Editor.meta


fileFormatVersion: 2
guid: 17ff74286bb818d44952b774377c8cc9
folderAsset: yes
timeCreated: 1507290633
licenseType: Pro
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

65
Tests/TextureCompressionTests/Editor/BC6HTests.cs


using UnityEngine;
using UnityEditor;
using NUnit.Framework;
using System.IO;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering;
public class BC6HTests
{
// Test BC6H fast encoding
[Test]
public void BC6HEncodeFast()
{
var shader = AssetDatabase.LoadAssetAtPath<ComputeShader>("Assets/ScriptableRenderPipeline/ScriptableRenderPipeline/Core/Resources/BC6H.compute");
var sourceTexture = AssetDatabase.LoadAssetAtPath<Texture2D>("Assets/ScriptableRenderPipeline/Tests/TextureCompressionTests/Editor/Resources/graffiti_shelter_1k.hdr");
var sourceTextureId = new RenderTargetIdentifier(sourceTexture);
var b = new BC6H(shader);
var target = b.InstantiateTarget(sourceTexture.width, sourceTexture.height);
target.Release();
target.enableRandomWrite = true;
target.Create();
var targetId = new RenderTargetIdentifier(target);
var cmb = new CommandBuffer { name = "TextureCompressionTests.BC6HEncodeFast" };
b.EncodeFast(cmb, sourceTextureId, sourceTexture.width, sourceTexture.height, targetId);
Graphics.ExecuteCommandBuffer(cmb);
var targetFile = "Assets/ScriptableRenderPipeline/Tests/TextureCompressionTests/Editor/Resources/graffiti_shelter_1k_bc6h.hdr";
var targetT2D = new Texture2D(target.width, target.height, TextureFormat.RGBAFloat, false, true);
Graphics.CopyTexture(target, targetT2D);
var bytes = targetT2D.GetRawTextureData();
File.WriteAllBytes(targetFile, bytes);
target.Release();
}
// Test texture copy
[Test]
public void TextureCopy()
{
var shader = AssetDatabase.LoadAssetAtPath<ComputeShader>("Assets/ScriptableRenderPipeline/ScriptableRenderPipeline/Core/Resources/BC6H.compute");
var sourceTexture = AssetDatabase.LoadAssetAtPath<Texture2D>("Assets/ScriptableRenderPipeline/Tests/TextureCompressionTests/Editor/Resources/graffiti_shelter_1k.hdr");
var k = shader.FindKernel("KCopy");
var target = new RenderTexture(sourceTexture.width, sourceTexture.height, 0, RenderTextureFormat.ARGBFloat, RenderTextureReadWrite.Linear);
target.Release();
target.enableRandomWrite = true;
target.Create();
var targetT2D = new Texture2D(sourceTexture.width, sourceTexture.height, TextureFormat.RGBAFloat, false, true);
shader.SetTexture(k, "_Source", sourceTexture);
shader.SetTexture(k, "_Target", target);
shader.Dispatch(k, targetT2D.width, targetT2D.height, 1);
Graphics.CopyTexture(target, targetT2D);
var targetFile = "Assets/ScriptableRenderPipeline/Tests/TextureCompressionTests/Editor/Resources/graffiti_shelter_1k_bc6h.hdr";
var bytes = targetT2D.GetRawTextureData();
File.WriteAllBytes(targetFile, bytes);
target.Release();
}
}

13
Tests/TextureCompressionTests/Editor/BC6HTests.cs.meta


fileFormatVersion: 2
guid: baee63d398484ee41837411594b77bfb
timeCreated: 1507290642
licenseType: Pro
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

10
Tests/TextureCompressionTests/Editor/Resources.meta


fileFormatVersion: 2
guid: 321ada41ece9a3d4390c83cc29284433
folderAsset: yes
timeCreated: 1507292432
licenseType: Pro
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

1001
Tests/TextureCompressionTests/Editor/Resources/graffiti_shelter_1k.hdr
文件差异内容过多而无法显示
查看文件

117
Tests/TextureCompressionTests/Editor/Resources/graffiti_shelter_1k.hdr.meta


fileFormatVersion: 2
guid: 01ab2e18bbd185c45b49fde0d95049fc
timeCreated: 1507294350
licenseType: Pro
TextureImporter:
fileIDToRecycleName: {}
externalObjects: {}
serializedVersion: 4
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: -1
aniso: -1
mipBias: -1
wrapU: -1
wrapV: -1
wrapW: -1
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spritePixelsToUnits: 100
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
platformSettings:
- buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
- buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
- buildTarget: iPhone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
- buildTarget: tvOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
- buildTarget: Windows Store Apps
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
spritePackingTag:
userData:
assetBundleName:
assetBundleVariant:
正在加载...
取消
保存