Skip to content

Commit f4d8723

Browse files
author
tsl
committed
adds GpuMat
1 parent 14ca9b8 commit f4d8723

File tree

10 files changed

+309
-4
lines changed

10 files changed

+309
-4
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ FetchContent_MakeAvailable(prgl)
4949
# modules
5050
add_subdirectory(painty/core)
5151
add_subdirectory(painty/image)
52+
add_subdirectory(painty/gpu)
5253
add_subdirectory(painty/io)
5354
add_subdirectory(painty/mixer)
5455
add_subdirectory(painty/renderer)

painty/core/Types.hxx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@
1414
#include <string>
1515

1616
namespace painty {
17+
18+
struct Size final {
19+
uint32_t width = {};
20+
uint32_t height = {};
21+
};
22+
1723
template <typename T>
1824
class DataType {
1925
public:

painty/gpu/BUILD.bazel

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
load("//:painty.bzl", "painty_cc_library", "painty_cc_test")
2+
3+
painty_cc_library(
4+
name = "painty_gpu",
5+
srcs = glob(["src/*.cxx"]),
6+
hdrs = glob(["*.hxx"]),
7+
visibility = ["//visibility:public"],
8+
deps = [
9+
"//painty/image:painty_image",
10+
],
11+
)
12+
13+
painty_cc_test(
14+
name = "test",
15+
srcs = glob(["test/src/*.cxx"]),
16+
deps = [
17+
":painty_gpu",
18+
"@gtest"
19+
]
20+
)

painty/gpu/CMakeLists.txt

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
cmake_minimum_required(VERSION 3.10.2)
2+
3+
project(paintyGpu)
4+
5+
add_library(${PROJECT_NAME} STATIC
6+
)
7+
8+
target_include_directories(${PROJECT_NAME}
9+
PUBLIC $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}>
10+
PUBLIC $<INSTALL_INTERFACE:include/>
11+
)
12+
13+
target_link_libraries(${PROJECT_NAME}
14+
PUBLIC paintyImage
15+
PUBLIC prgl
16+
)
17+
18+
add_dependencies(${PROJECT_NAME}
19+
paintyImage
20+
prgl
21+
)
22+
23+
set_target_properties(${PROJECT_NAME} PROPERTIES CXX_STANDARD 17)
24+
set_target_properties(${PROJECT_NAME} PROPERTIES CXX_STANDARD_REQUIRED ON)
25+
set_target_properties(${PROJECT_NAME} PROPERTIES DEBUG_POSTFIX "d")
26+
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
27+
# using Clang
28+
target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Weverything -Wno-c++98-compat -Wno-padded -Wno-documentation -Werror -Wno-global-constructors)
29+
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
30+
# using GCC
31+
target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Werror)
32+
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
33+
# using Visual Studio C++
34+
target_compile_options(${PROJECT_NAME} PRIVATE /W4 /WX)
35+
endif()
36+
if(RUN_TESTS)
37+
add_subdirectory(test)
38+
endif()

painty/gpu/GpuMat.hxx

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
/**
2+
* @file GpuMat.hxx
3+
* @author thomas lindemeier
4+
* @brief
5+
* @date 2020-10-08
6+
*
7+
*/
8+
#pragma once
9+
10+
#include "painty/core/Types.hxx"
11+
#include "painty/image/Mat.hxx"
12+
#include "prgl/Texture2d.hxx"
13+
14+
namespace painty {
15+
16+
template <class T>
17+
class GpuMat final {
18+
public:
19+
GpuMat(const Size& size) : _size(size) {}
20+
GpuMat(const Mat<T>& cpuImage)
21+
: _size({static_cast<uint32_t>(cpuImage.cols),
22+
static_cast<uint32_t>(cpuImage.rows)}) {
23+
upload(cpuImage);
24+
}
25+
26+
void upload(const Mat<T>& m) {
27+
_mat = m;
28+
29+
if ((_texture == nullptr) ||
30+
(static_cast<uint32_t>(_mat.cols) != _size.width) ||
31+
(static_cast<uint32_t>(_mat.rows) != _size.height)) {
32+
const auto type =
33+
prgl::DataTypeTr<typename DataType<T>::channel_type>::dataType;
34+
const auto nrChannels = DataType<T>::dim;
35+
36+
auto internalFormat = prgl::TextureFormatInternal::Rgb32F;
37+
auto format = prgl::TextureFormat::Rgb;
38+
switch (type) {
39+
case prgl::DataType::UnsignedByte: {
40+
if (nrChannels == 1U) {
41+
format = prgl::TextureFormat::Red;
42+
internalFormat = prgl::TextureFormatInternal::R8Ui;
43+
} else if (nrChannels == 2U) {
44+
format = prgl::TextureFormat::Rg;
45+
internalFormat = prgl::TextureFormatInternal::Rg8Ui;
46+
} else if (nrChannels == 3U) {
47+
format = prgl::TextureFormat::Rgb;
48+
internalFormat = prgl::TextureFormatInternal::Rgb8Ui;
49+
} else if (nrChannels == 4U) {
50+
format = prgl::TextureFormat::Rgba;
51+
internalFormat = prgl::TextureFormatInternal::Rgba8Ui;
52+
}
53+
break;
54+
}
55+
case prgl::DataType::UnsignedShort: {
56+
if (nrChannels == 1U) {
57+
format = prgl::TextureFormat::Red;
58+
internalFormat = prgl::TextureFormatInternal::R16Ui;
59+
} else if (nrChannels == 2U) {
60+
format = prgl::TextureFormat::Rg;
61+
internalFormat = prgl::TextureFormatInternal::Rg16Ui;
62+
} else if (nrChannels == 3U) {
63+
format = prgl::TextureFormat::Rgb;
64+
internalFormat = prgl::TextureFormatInternal::Rgb16Ui;
65+
} else if (nrChannels == 4U) {
66+
format = prgl::TextureFormat::Rgba;
67+
internalFormat = prgl::TextureFormatInternal::Rgba16Ui;
68+
}
69+
break;
70+
}
71+
case prgl::DataType::Float:
72+
case prgl::DataType::Double: {
73+
if (nrChannels == 1U) {
74+
format = prgl::TextureFormat::Red;
75+
internalFormat = prgl::TextureFormatInternal::R32F;
76+
} else if (nrChannels == 2U) {
77+
format = prgl::TextureFormat::Rg;
78+
internalFormat = prgl::TextureFormatInternal::Rg32F;
79+
} else if (nrChannels == 3U) {
80+
format = prgl::TextureFormat::Rgb;
81+
internalFormat = prgl::TextureFormatInternal::Rgb32F;
82+
} else if (nrChannels == 4U) {
83+
format = prgl::TextureFormat::Rgba;
84+
internalFormat = prgl::TextureFormatInternal::Rgba32F;
85+
}
86+
break;
87+
}
88+
case prgl::DataType::Byte:
89+
case prgl::DataType::Short:
90+
case prgl::DataType::UnsignedInt:
91+
case prgl::DataType::Int:
92+
case prgl::DataType::HalfFloat: {
93+
throw std::invalid_argument("Mat type not supported by GpuMat.");
94+
break;
95+
}
96+
}
97+
98+
const auto minFilter = prgl::TextureMinFilter::Linear;
99+
const auto magFilter = prgl::TextureMagFilter::Linear;
100+
const auto envMode = prgl::TextureEnvMode::Replace;
101+
const auto wrapMode = prgl::TextureWrapMode::Repeat;
102+
103+
_texture = prgl::Texture2d::Create(
104+
_size.width, _size.height, internalFormat, format, type, minFilter,
105+
magFilter, envMode, wrapMode, false);
106+
}
107+
108+
Mat<T> yFlipped(_mat.size());
109+
cv::flip(_mat, yFlipped, 0);
110+
111+
_texture->upload(yFlipped.data);
112+
}
113+
114+
auto getMat() const -> const Mat<T>& {
115+
return _mat;
116+
}
117+
118+
auto getSize() const -> const Size& {
119+
return _size;
120+
}
121+
122+
auto getTexture() -> const std::shared_ptr<prgl::Texture2d>& {
123+
return _texture;
124+
}
125+
126+
void download() {
127+
const auto type =
128+
prgl::DataTypeTr<typename DataType<T>::channel_type>::dataType;
129+
const auto nrChannels = DataType<T>::dim;
130+
131+
if ((static_cast<uint32_t>(_mat.cols) != _size.width) ||
132+
(static_cast<uint32_t>(_mat.rows) != _size.height)) {
133+
_mat = Mat<T>(static_cast<int32_t>(_size.height),
134+
static_cast<int32_t>(_size.width));
135+
}
136+
auto format = prgl::TextureFormat::Red;
137+
if (nrChannels == 1U) {
138+
format = prgl::TextureFormat::Red;
139+
} else if (nrChannels == 2U) {
140+
format = prgl::TextureFormat::Rg;
141+
} else if (nrChannels == 3U) {
142+
format = prgl::TextureFormat::Rgb;
143+
} else if (nrChannels == 4U) {
144+
format = prgl::TextureFormat::Rgba;
145+
}
146+
_texture->download(&(_mat.data), format, type);
147+
148+
cv::flip(_mat, _mat, 0);
149+
}
150+
151+
private:
152+
Size _size;
153+
154+
std::shared_ptr<prgl::Texture2d> _texture = nullptr;
155+
156+
Mat<T> _mat = {};
157+
}; // namespace painty
158+
} // namespace painty

painty/gpu/test/CMakeLists.txt

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
cmake_minimum_required(VERSION 3.10.2)
2+
3+
project(paintyGpuTest)
4+
5+
add_executable(${PROJECT_NAME}
6+
${PROJECT_SOURCE_DIR}/src/GpuMatTest.cxx
7+
${PROJECT_SOURCE_DIR}/src/main.cxx
8+
)
9+
10+
add_test(
11+
NAME ${PROJECT_NAME}
12+
COMMAND ${PROJECT_NAME}
13+
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
14+
)
15+
16+
target_link_libraries(${PROJECT_NAME}
17+
gtest
18+
paintyGpu
19+
)
20+
add_dependencies(${PROJECT_NAME}
21+
gtest
22+
paintyGpu
23+
)
24+
set_target_properties(${PROJECT_NAME} PROPERTIES CXX_STANDARD 17)
25+
set_target_properties(${PROJECT_NAME} PROPERTIES CXX_STANDARD_REQUIRED ON)
26+
set_target_properties(${PROJECT_NAME} PROPERTIES DEBUG_POSTFIX "d")
27+
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
28+
# using Clang
29+
target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Weverything -Wno-c++98-compat -Wno-padded -Wno-documentation -Werror -Wno-global-constructors)
30+
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
31+
# using GCC
32+
target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Werror)
33+
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
34+
# using Visual Studio C++
35+
target_compile_options(${PROJECT_NAME} PRIVATE /W4 /WX)
36+
endif()

painty/gpu/test/src/GpuMatTest.cxx

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/**
2+
* @file GpuMatTest.cxx
3+
* @author thomas lindemeier
4+
*
5+
* @brief
6+
*
7+
* @date 2020-10-08
8+
*
9+
*/
10+
#include "gtest/gtest.h"
11+
#include "painty/gpu/GpuMat.hxx"
12+
#include "prgl/Window.hxx"
13+
14+
TEST(GpuMatTest, Construct) {
15+
std::make_shared<prgl::Window>(1024, 768, "window", true);
16+
17+
painty::Mat1f cpuMat(768, 1024);
18+
{
19+
uint32_t i = 0U;
20+
for (auto& a : cpuMat) {
21+
a = static_cast<float>(i++) / static_cast<float>(cpuMat.total());
22+
}
23+
}
24+
25+
auto cpuMatClone = cpuMat.clone();
26+
auto gpuMat = painty::GpuMat<float>(cpuMatClone);
27+
gpuMat.download();
28+
29+
const auto cpuMatRead = gpuMat.getMat();
30+
31+
EXPECT_EQ(cpuMatRead.size(), cpuMatClone.size());
32+
33+
for (auto l = 0; l < static_cast<int32_t>(cpuMatClone.total()); l++) {
34+
EXPECT_NEAR(cpuMatRead(l), cpuMatClone(l), 0.00001F);
35+
}
36+
}

painty/gpu/test/src/main.cxx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#include "gtest/gtest.h"
2+
3+
int main(int argc, char** argv) {
4+
testing::InitGoogleTest(&argc, argv);
5+
6+
return RUN_ALL_TESTS();
7+
}

painty/renderer/CMakeLists.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ add_library(${PROJECT_NAME} STATIC
77
${PROJECT_SOURCE_DIR}/src/BrushStrokeSample.cxx
88
${PROJECT_SOURCE_DIR}/src/TextureBrushDictionary.cxx
99
${PROJECT_SOURCE_DIR}/src/TextureBrushGpu.cxx
10+
${PROJECT_SOURCE_DIR}/src/PaintLayerGpu.cxx
1011
)
1112

1213
target_include_directories(${PROJECT_NAME}
@@ -16,17 +17,17 @@ target_include_directories(${PROJECT_NAME}
1617

1718
target_link_libraries(${PROJECT_NAME}
1819
paintyCore
20+
paintyGpu
1921
paintyImage
2022
paintyIo
2123
stdc++fs
22-
prgl
2324
)
2425

2526
add_dependencies(${PROJECT_NAME}
2627
paintyCore
28+
paintyGpu
2729
paintyImage
2830
paintyIo
29-
prgl
3031
)
3132

3233
set_target_properties(${PROJECT_NAME} PROPERTIES CXX_STANDARD 17)

painty/renderer/src/TextureBrushGpu.cxx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,10 @@ void painty::TextureBrushGpu::paintStroke(const std::vector<vec2>& verticesArg,
150150
}
151151
}
152152
}
153-
std::vector<float> warpedBrushTextureData;
154-
warpedBrushTexture->download(warpedBrushTextureData);
153+
std::vector<float> warpedBrushTextureData(warpedBrushTexture->getHeight() *
154+
warpedBrushTexture->getWidth());
155+
warpedBrushTexture->download(warpedBrushTextureData.data(),
156+
prgl::TextureFormat::Red, prgl::DataType::Float);
155157

156158
Mat1d texDataMat(static_cast<int32_t>(warpedBrushTexture->getHeight()),
157159
static_cast<int32_t>(warpedBrushTexture->getWidth()));

0 commit comments

Comments
 (0)