Skip to content

Commit 076b2aa

Browse files
committed
Capture image properties in YAML
This refactors some code throughout the offloader to read the output properties from the YAML. This enables naming the output and also specifying color depth and image dimensions for the PNG writer.
1 parent 5279e11 commit 076b2aa

File tree

5 files changed

+79
-47
lines changed

5 files changed

+79
-47
lines changed

include/API/Pipeline.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "llvm/ADT/StringRef.h"
1515
#include "llvm/Support/YAMLTraits.h"
1616
#include <memory>
17+
#include <string>
1718

1819
namespace hlsltest {
1920

@@ -43,6 +44,13 @@ struct DirectXBinding {
4344
uint32_t Space;
4445
};
4546

47+
struct OutputProperties {
48+
std::string Name;
49+
int Height;
50+
int Width;
51+
int Depth;
52+
};
53+
4654
struct Resource {
4755
DataFormat Format;
4856
int Channels;
@@ -51,6 +59,7 @@ struct Resource {
5159
size_t Size;
5260
std::unique_ptr<char[]> Data;
5361
DirectXBinding DXBinding;
62+
OutputProperties OutputProps;
5463

5564
bool isRaw() const {
5665
return RawSize > 0;
@@ -124,6 +133,10 @@ template <> struct MappingTraits<hlsltest::DirectXBinding> {
124133
static void mapping(IO &I, hlsltest::DirectXBinding &B);
125134
};
126135

136+
template <> struct MappingTraits<hlsltest::OutputProperties> {
137+
static void mapping(IO &I, hlsltest::OutputProperties &P);
138+
};
139+
127140
template <> struct ScalarEnumerationTraits<hlsltest::DataFormat> {
128141
static void enumeration(IO &I, hlsltest::DataFormat &V) {
129142
#define ENUM_CASE(Val) I.enumCase(V, #Val, hlsltest::DataFormat::Val)

lib/API/Pipeline.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,23 @@ void MappingTraits<hlsltest::Resource>::mapping(IO &I, hlsltest::Resource &R) {
6969
DATA_CASE(Float32, float)
7070
DATA_CASE(Float64, double)
7171
}
72+
7273
I.mapRequired("DirectXBinding", R.DXBinding);
74+
I.mapOptional("OutputProps", R.OutputProps);
7375
}
7476

7577
void MappingTraits<hlsltest::DirectXBinding>::mapping(
7678
IO &I, hlsltest::DirectXBinding &B) {
7779
I.mapRequired("Register", B.Register);
7880
I.mapRequired("Space", B.Space);
7981
}
82+
83+
void MappingTraits<hlsltest::OutputProperties>::mapping(
84+
IO &I, hlsltest::OutputProperties &P) {
85+
I.mapRequired("Name", P.Name);
86+
I.mapRequired("Height", P.Height);
87+
I.mapRequired("Width", P.Width);
88+
I.mapRequired("Depth", P.Depth);
89+
}
8090
} // namespace yaml
8191
} // namespace llvm

test/Basic/Mandelbrot.test

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,17 +64,23 @@ DescriptorSets:
6464
DirectXBinding:
6565
Register: 0
6666
Space: 0
67+
OutputProps:
68+
Name: Output
69+
Height: 4096
70+
Width: 4096
71+
Depth: 16
6772
...
6873
#--- end
6974

70-
# REQUIRES: imagediff
7175
# this test doesn't yet do anything... so we should skip it.
7276

77+
# REQUIRES: goldenimage
7378
# RUN: split-file %s %t
7479
# RUN: %if DirectX %{ dxc -T cs_6_0 -Fo %t.dxil %t/source.hlsl %}
75-
# RUN: %if DirectX %{ %offloader %t/pipeline.yaml %t.dxil | FileCheck %s %}
80+
# RUN: %if DirectX %{ %offloader %t/pipeline.yaml %t.dxil -r Output -o %t/output.png %}
7681
# RUN: %if Vulkan %{ dxc -T cs_6_0 -spirv -Fo %t.spv %t/source.hlsl %}
77-
# RUN: %if Vulkan %{ %offloader %t/pipeline.yaml %t.spv | FileCheck %s %}
82+
# RUN: %if Vulkan %{ %offloader %t/pipeline.yaml %t.spv -r Output -o %t/output.png %}
7883
# RUN: %if Metal %{ dxc -T cs_6_0 -Fo %t.dxil %t/source.hlsl %}
7984
# RUN: %if Metal %{ metal-shaderconverter %t.dxil -o=%t.metallib %}
80-
# RUN: %if Metal %{ %offloader %t/pipeline.yaml %t.metallib | FileCheck %s %}
85+
# RUN: %if Metal %{ %offloader %t/pipeline.yaml %t.metallib -r Output -o %t/output.png %}
86+
# RUN: cmp %t/output.png %goldenimage_dir/hlsl/Basic/Mandelbrot.png

tools/offloader/WritePNG.cpp

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,30 +13,46 @@
1313
#include "llvm/ADT/ScopeExit.h"
1414
#include "llvm/ADT/StringRef.h"
1515
#include "llvm/Support/Error.h"
16+
#include "llvm/Support/SwapByteOrder.h"
1617

18+
#include <limits.h>
1719
#include <png.h>
1820
#include <stdio.h>
1921

2022
using namespace hlsltest;
2123

24+
template <typename DstType, typename SrcType>
25+
void TranslatePixelData(DstType *Dst, const SrcType *Src, uint64_t Count) {
26+
for (uint64_t I = 0; I < Count; ++I, ++Src, ++Dst) {
27+
SrcType Val = *Src;
28+
Val *= std::numeric_limits<DstType>::max();
29+
*Dst = static_cast<DstType>(Val);
30+
if (sizeof(DstType) > 1 && !llvm::sys::IsBigEndianHost)
31+
llvm::sys::swapByteOrder(*Dst);
32+
}
33+
}
34+
2235
llvm::Error WritePNG(llvm::StringRef OutputPath, const Resource &R) {
23-
// TODO: Fixme
24-
int Height = 4096;
25-
int Width = 4096;
36+
int Height = R.OutputProps.Height;
37+
int Width = R.OutputProps.Width;
38+
int Depth = R.OutputProps.Depth;
39+
assert(Depth == 16 || Depth == 8 && "Color depth should be 8 or 16 bits.");
2640
// Translate pixel data into uint8_t.
2741
uint64_t Stride = R.getSingleElementSize();
2842
uint64_t PixelComponents = R.Size / Stride;
2943
std::unique_ptr<uint8_t[]> PixelData =
30-
std::make_unique<uint8_t[]>(PixelComponents);
44+
std::make_unique<uint8_t[]>(PixelComponents * (Depth / 8));
3145

32-
const uint8_t *Src = reinterpret_cast<const uint8_t *>(R.Data.get());
3346
switch (R.Format) {
3447
case DataFormat::Float32:
35-
for (uint64_t I = 0; I < PixelComponents; ++I, Src += Stride) {
36-
float Val = *reinterpret_cast<const float *>(Src);
37-
Val *= 255.f;
38-
PixelData[I] = static_cast<uint8_t>(Val);
39-
}
48+
if (Depth == 16)
49+
TranslatePixelData(reinterpret_cast<uint16_t *>(PixelData.get()),
50+
reinterpret_cast<const float *>(R.Data.get()),
51+
PixelComponents);
52+
else
53+
TranslatePixelData(reinterpret_cast<uint8_t *>(PixelData.get()),
54+
reinterpret_cast<const float *>(R.Data.get()),
55+
PixelComponents);
4056
break;
4157
default:
4258
llvm_unreachable("Unsupported format for png output");
@@ -63,7 +79,7 @@ llvm::Error WritePNG(llvm::StringRef OutputPath, const Resource &R) {
6379
if (!F)
6480
return llvm::createStringError(std::errc::io_error, "Failed openiong file");
6581
png_init_io(PNG, F);
66-
png_set_IHDR(PNG, PNGInfo, Width, Height, 8, PNG_COLOR_TYPE_RGB_ALPHA,
82+
png_set_IHDR(PNG, PNGInfo, Width, Height, Depth, PNG_COLOR_TYPE_RGB_ALPHA,
6783
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
6884
PNG_FILTER_TYPE_BASE);
6985
png_colorp ColorP =
@@ -76,9 +92,10 @@ llvm::Error WritePNG(llvm::StringRef OutputPath, const Resource &R) {
7692
png_set_packing(PNG);
7793

7894
png_bytepp Rows = (png_bytepp)png_malloc(PNG, Height * sizeof(png_bytep));
79-
uint64_t RowSize = Width * R.Channels;
95+
uint64_t DepthBytes = Depth / 8;
96+
uint64_t RowSize = Width * R.Channels * DepthBytes;
8097
// Step one row back from the end
81-
uint8_t *Row = PixelData.get() + PixelComponents - RowSize;
98+
uint8_t *Row = PixelData.get() + (PixelComponents * DepthBytes) - RowSize;
8299
for (int I = 0; I < Height; ++I, Row -= RowSize)
83100
Rows[I] = (png_bytep)Row;
84101

tools/offloader/offloader.cpp

Lines changed: 16 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,14 @@ static cl::opt<std::string> OutputFilename("o", cl::desc("Output filename"),
4444
cl::value_desc("filename"),
4545
cl::init("-"));
4646

47-
static cl::opt<std::string> ImageOutput("r",
48-
cl::desc("Resource index to output"),
49-
cl::value_desc("set,resource"),
50-
cl::init(""));
47+
static cl::opt<std::string>
48+
ImageOutput("r", cl::desc("Resource name to output as png"),
49+
cl::value_desc("<name>"), cl::init(""));
5150

5251
static cl::opt<bool>
5352
Quiet("quiet", cl::desc("Suppress printing the pipeline as output"));
5453

55-
static cl::opt<bool>
56-
UseWarp("warp", cl::desc("Use warp"));
54+
static cl::opt<bool> UseWarp("warp", cl::desc("Use warp"));
5755

5856
llvm::Error WritePNG(llvm::StringRef, const Resource &);
5957

@@ -101,9 +99,8 @@ int run() {
10199
}
102100

103101
if (UseWarp && APIToUse != GPUAPI::DirectX)
104-
ExitOnErr(
105-
createStringError(std::errc::executable_format_error,
106-
"WARP required DirectX API"));
102+
ExitOnErr(createStringError(std::errc::executable_format_error,
103+
"WARP required DirectX API"));
107104

108105
if (APIToUse == GPUAPI::Unknown)
109106
ExitOnErr(
@@ -139,28 +136,17 @@ int run() {
139136
Out->keep();
140137
return 0;
141138
}
142-
llvm::Regex R("^([0-9]+),([0-9]+)$");
143-
llvm::SmallVector<llvm::StringRef, 2> Matches;
144-
if (!R.match(ImageOutput, &Matches))
145-
ExitOnErr(
146-
createStringError(std::errc::invalid_argument,
147-
"Image output argument must be specified as "
148-
"<set>,<resource> (e.g. \"0,1\", \"3,0\", etc)"));
149-
uint64_t Set = 0, Resource = 0;
150-
if (!to_integer(Matches[1], Set) || !to_integer(Matches[2], Resource))
151-
ExitOnErr(
152-
createStringError(std::errc::invalid_argument,
153-
"Image output argument must be specified as "
154-
"<set>,<resource> (e.g. \"0,1\", \"3,0\", etc)"));
155-
if (Set >= PipelineDesc.Sets.size())
156-
ExitOnErr(createStringError(std::errc::invalid_argument,
157-
"Specified descriptor set out of range"));
158-
if (Resource >= PipelineDesc.Sets[Set].Resources.size())
159-
ExitOnErr(createStringError(std::errc::invalid_argument,
160-
"Specified descriptor index out of range"));
139+
for (const auto &S : PipelineDesc.Sets) {
140+
for (const auto &R : S.Resources) {
141+
if (R.OutputProps.Name == ImageOutput) {
142+
ExitOnErr(WritePNG(OutputFilename, R));
143+
return 0;
144+
}
145+
}
146+
}
147+
161148
ExitOnErr(
162-
WritePNG(OutputFilename, PipelineDesc.Sets[Set].Resources[Resource]));
163-
return 0;
149+
createStringError(Twine("No descriptor with name ") + ImageOutput));
164150
}
165151
return 1;
166152
}

0 commit comments

Comments
 (0)