1 /**
2 * Copyright 2021 Huawei Technologies Co., Ltd
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "tools/common/opengl_util.h"
18 #include <cstdlib>
19 #include <algorithm>
20
21 namespace mindspore {
22 namespace OpenGL {
23 #if defined(GPU_OPENCL) && defined(__ANDROID__) && defined(ENABLE_ARM64)
24 const char *g_glsl_host_to_device_2d =
25 "#version 320 es\n"
26 "#define PRECISION highp\n"
27 "precision PRECISION float;\n"
28 "#define FORMAT rgba32f\n"
29 "layout(FORMAT, binding=0) writeonly uniform PRECISION image2D uImage;\n"
30 "layout(binding=1) readonly buffer SSBO {\n"
31 " float data[];\n"
32 "} uInBuffer;\n"
33 "layout(location = 2) uniform int uWidth;\n"
34 "layout(location = 3) uniform int uHeight;\n"
35 "layout(location = 4) uniform int uChannel;\n"
36 "layout (local_size_x = 4, local_size_y = 4, local_size_z = 1) in;\n"
37 "void main()\n"
38 "{\n"
39 " ivec3 pos = ivec3(gl_GlobalInvocationID);\n"
40 " if (pos.x < uWidth && pos.y < uHeight)\n"
41 " {\n"
42 " vec4 color;\n"
43 " color.r = uInBuffer.data[pos.y*uWidth*uChannel + pos.x*uChannel + 0];\n"
44 " color.g = uInBuffer.data[pos.y*uWidth*uChannel + pos.x*uChannel + 1];\n"
45 " color.b = uInBuffer.data[pos.y*uWidth*uChannel + pos.x*uChannel + 2];\n"
46 " color.a = uInBuffer.data[pos.y*uWidth*uChannel + pos.x*uChannel + 3];\n"
47 " imageStore(uImage, pos.xy, color);\n"
48 " }\n"
49 "}\n";
50
51 const char *g_glsl_host_to_device_3d =
52 "#version 320 es\n"
53 "#define PRECISION highp\n"
54 "precision PRECISION float;\n"
55 "#define FORMAT rgba32f\n"
56 "layout(FORMAT, binding=0) writeonly uniform PRECISION image3D uImage;\n"
57 "layout(binding=1) readonly buffer SSBO {\n"
58 " float data[];\n"
59 "} uInBuffer;\n"
60 "layout(location = 2) uniform int uWidth;\n"
61 "layout(location = 3) uniform int uHeight;\n"
62 "layout(location = 4) uniform int uChannel;\n"
63 "layout (local_size_x = 4, local_size_y = 4, local_size_z = 1) in;\n"
64 "void main()\n"
65 "{\n"
66 " ivec3 pos = ivec3(gl_GlobalInvocationID);\n"
67 " if (pos.x < uWidth && pos.y < uHeight)\n"
68 " {\n"
69 " vec4 color;\n"
70 " int z = pos.z*4;\n"
71 " color.r = uInBuffer.data[pos.y*uWidth*uChannel + pos.x*uChannel + (z+0)];\n"
72 " color.g = uInBuffer.data[pos.y*uWidth*uChannel + pos.x*uChannel + (z+1)];\n"
73 " color.b = uInBuffer.data[pos.y*uWidth*uChannel + pos.x*uChannel + (z+2)];\n"
74 " color.a = uInBuffer.data[pos.y*uWidth*uChannel + pos.x*uChannel + (z+3)];\n"
75 " imageStore(uImage, pos, color);\n"
76 " }\n"
77 "}\n";
78
79 const char *g_glsl_device_to_host_2d =
80 "#version 320 es\n"
81 "#define PRECISION highp\n"
82 "precision PRECISION float;\n"
83 "#define FORMAT rgba32f\n"
84 "layout(FORMAT, binding=0) readonly uniform PRECISION image2D uImage;\n"
85 "layout(binding=1) writeonly buffer destBuffer{\n"
86 " float data[];\n"
87 "} uOutBuffer;\n"
88 "layout(location = 2) uniform int uWidth;\n"
89 "layout(location = 3) uniform int uHeight;\n"
90 "layout(location = 4) uniform int uChannel;\n"
91 "layout (local_size_x = 4, local_size_y = 4, local_size_z = 1) in;\n"
92 "#define UP_DIV(x, y) (((x) + (y) - (1)) / (y))\n"
93 "void main()\n"
94 "{\n"
95 " ivec3 pos = ivec3(gl_GlobalInvocationID);\n"
96 " if (pos.x < uWidth && pos.y < uHeight)\n"
97 " {\n"
98 " vec4 color = imageLoad(uImage, ivec2(pos.x * UP_DIV(uChannel, 4) + pos.z, pos.y));\n"
99 " int z = pos.z*4;\n"
100 " for (int i = 0; i < 4; i++) {\n"
101 " if (z + i < uChannel) {\n"
102 " uOutBuffer.data[pos.y*uWidth*uChannel+pos.x*uChannel+(z+i)] = color[i];\n"
103 " } \n"
104 " } \n"
105 " }\n"
106 "}\n";
107
108 const char *g_glsl_device_to_host_3d =
109 "#version 320 es\n"
110 "#define PRECISION highp\n"
111 "precision PRECISION float;\n"
112 "#define FORMAT rgba32f\n"
113 "layout(FORMAT, binding=0) readonly uniform PRECISION image3D uImage;\n"
114 "layout(binding=1) writeonly buffer destBuffer{\n"
115 " float data[];\n"
116 "} uOutBuffer;\n"
117 "layout(location = 2) uniform int uWidth;\n"
118 "layout(location = 3) uniform int uHeight;\n"
119 "layout(location = 4) uniform int uChannel;\n"
120 "layout (local_size_x = 4, local_size_y = 4, local_size_z = 1) in;\n"
121 "void main()\n"
122 "{\n"
123 " ivec3 pos = ivec3(gl_GlobalInvocationID);\n"
124 " if (pos.x < uWidth && pos.y < uHeight)\n"
125 " {\n"
126 " vec4 color = imageLoad(uImage, pos);\n"
127 " int z = pos.z*4;\n"
128 " uOutBuffer.data[pos.y*uWidth*uChannel+pos.x*uChannel+(z+0)] = color.r;\n"
129 " uOutBuffer.data[pos.y*uWidth*uChannel+pos.x*uChannel+(z+1)] = color.g;\n"
130 " uOutBuffer.data[pos.y*uWidth*uChannel+pos.x*uChannel+(z+2)] = color.b;\n"
131 " uOutBuffer.data[pos.y*uWidth*uChannel+pos.x*uChannel+(z+3)] = color.a;\n"
132 " }\n"
133 "}\n";
134
135 constexpr int kC4Align = 4;
136 constexpr int kWidthIndex = 0;
137 constexpr int kHeightIndex = 1;
138 constexpr int kChannelIndex = 2;
139
Init()140 bool OpenGLRuntime::Init() {
141 MS_LOG(INFO) << "Rt Init Begin";
142 if (!(eglGetCurrentContext() != EGL_NO_CONTEXT)) {
143 m_display_ = eglGetDisplay(EGL_DEFAULT_DISPLAY);
144 if (m_display_ == EGL_NO_DISPLAY) {
145 MS_LOG(ERROR) << "eglGetDisplay error";
146 return false;
147 }
148
149 int majorVersion;
150 int minorVersion;
151 auto glRet = eglInitialize(m_display_, &majorVersion, &minorVersion);
152 if (glRet != EGL_TRUE) {
153 MS_LOG(ERROR) << "eglInitialize error";
154 return false;
155 }
156
157 EGLint numConfigs;
158 static const EGLint configAttribs[] = {EGL_SURFACE_TYPE,
159 EGL_PBUFFER_BIT,
160 EGL_RENDERABLE_TYPE,
161 EGL_OPENGL_ES2_BIT,
162 EGL_RED_SIZE,
163 8,
164 EGL_GREEN_SIZE,
165 8,
166 EGL_BLUE_SIZE,
167 8,
168 EGL_ALPHA_SIZE,
169 8,
170 EGL_NONE};
171
172 EGLConfig surfaceConfig;
173 if (!eglChooseConfig(m_display_, configAttribs, &surfaceConfig, 1, &numConfigs)) {
174 eglMakeCurrent(m_display_, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
175 eglTerminate(m_display_);
176 m_display_ = EGL_NO_DISPLAY;
177 MS_LOG(ERROR) << "eglChooseConfig error";
178 return false;
179 }
180
181 static const EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE};
182 m_context_ = eglCreateContext(m_display_, surfaceConfig, NULL, contextAttribs);
183 static const EGLint surfaceAttribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE};
184 m_surface_ = eglCreatePbufferSurface(m_display_, surfaceConfig, surfaceAttribs);
185
186 glRet = eglMakeCurrent(m_display_, m_surface_, m_surface_, m_context_);
187 if (glRet != EGL_TRUE) {
188 MS_LOG(ERROR) << "eglMakeCurrent error";
189 return false;
190 }
191
192 eglBindAPI(EGL_OPENGL_ES_API);
193 } else {
194 m_context_ = EGL_NO_CONTEXT;
195 MS_LOG(ERROR) << "eglGetCurrentContext() != EGL_NO_CONTEXT";
196 return false;
197 }
198 MS_LOG(INFO) << "Rt Init End";
199 return true;
200 }
201
LoadShader(GLenum shaderType,const char * pSource)202 GLuint OpenGLRuntime::LoadShader(GLenum shaderType, const char *pSource) {
203 GLuint shader = glCreateShader(shaderType);
204 if (shader) {
205 glShaderSource(shader, 1, &pSource, NULL);
206 glCompileShader(shader);
207 GLint compiled = 0;
208 glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
209 if (!compiled) {
210 GLint infoLen = 0;
211 glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
212 if (infoLen > 0) {
213 char *buf = reinterpret_cast<char *>(malloc(infoLen));
214 MS_CHECK_TRUE_MSG(buf != nullptr, 0, "Malloc OpenGL Buffer failed");
215 glGetShaderInfoLog(shader, infoLen, NULL, buf);
216 fprintf(stderr, "Could not compile shader %d:\n%s\n", shaderType, buf);
217 free(buf);
218 glDeleteShader(shader);
219 shader = 0;
220 }
221 }
222 }
223 return shader;
224 }
225
CreateComputeProgram(const char * pComputeSource)226 GLuint OpenGLRuntime::CreateComputeProgram(const char *pComputeSource) {
227 GLuint computeShader = LoadShader(GL_COMPUTE_SHADER, pComputeSource);
228 if (!computeShader) {
229 return 0;
230 }
231
232 GLuint program = glCreateProgram();
233 if (program) {
234 glAttachShader(program, computeShader);
235 glLinkProgram(program);
236 GLint linkStatus = GL_FALSE;
237 glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
238 if (linkStatus != GL_TRUE) {
239 GLint bufLength = 0;
240 glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
241 if (bufLength > 0) {
242 char *buf = reinterpret_cast<char *>(malloc(bufLength));
243 MS_CHECK_TRUE_MSG(buf != nullptr, 0, "Malloc OpenGL Buffer failed");
244 glGetProgramInfoLog(program, bufLength, NULL, buf);
245 fprintf(stderr, "Could not link program:\n%s\n", buf);
246 free(buf);
247 }
248 glDeleteProgram(program);
249 program = 0;
250 }
251 }
252 return program;
253 }
254
GLCreateSSBO(GLsizeiptr size,void * hostData,GLenum type,GLenum usage)255 GLuint OpenGLRuntime::GLCreateSSBO(GLsizeiptr size, void *hostData, GLenum type, GLenum usage) {
256 MS_ASSERT(size > 0);
257
258 GLuint ssboBufferID;
259 glGenBuffers(1, &ssboBufferID);
260 OPENGL_CHECK_ERROR;
261
262 glBindBuffer(type, ssboBufferID);
263 OPENGL_CHECK_ERROR;
264 MS_ASSERT(ssboBufferID > 0);
265
266 glBufferData(type, size, hostData, usage);
267 OPENGL_CHECK_ERROR;
268
269 MS_ASSERT(m_ssbo_pool_.count(ssboBufferID) == 0);
270 m_ssbo_pool_[ssboBufferID] = std::make_pair(size, type);
271
272 return ssboBufferID;
273 }
274
CopyDeviceSSBOToHost(GLuint ssboBufferID,void * hostData,GLsizeiptr size)275 bool OpenGLRuntime::CopyDeviceSSBOToHost(GLuint ssboBufferID, void *hostData, GLsizeiptr size) {
276 MS_ASSERT(m_ssbo_pool_.count(ssboBufferID) > 0);
277 MS_ASSERT(m_ssbo_pool_[ssboBufferID].first >= size);
278
279 glBindBuffer(m_ssbo_pool_[ssboBufferID].second, ssboBufferID);
280 OPENGL_CHECK_ERROR;
281
282 auto ptr = glMapBufferRange(m_ssbo_pool_[ssboBufferID].second, 0, m_ssbo_pool_[ssboBufferID].first, GL_MAP_READ_BIT);
283 OPENGL_CHECK_ERROR;
284
285 if (ptr != nullptr) {
286 ::memcpy(hostData, ptr, size);
287 }
288
289 glUnmapBuffer(m_ssbo_pool_[ssboBufferID].second);
290 OPENGL_CHECK_ERROR;
291 return true;
292 }
293
CopyHostToDeviceSSBO(void * hostData,GLuint ssboBufferID,GLsizeiptr size)294 bool OpenGLRuntime::CopyHostToDeviceSSBO(void *hostData, GLuint ssboBufferID, GLsizeiptr size) {
295 MS_ASSERT(m_ssbo_pool_.count(ssboBufferID) > 0);
296 MS_ASSERT(m_ssbo_pool_[ssboBufferID].first >= size);
297
298 glBindBuffer(m_ssbo_pool_[ssboBufferID].second, ssboBufferID);
299 OPENGL_CHECK_ERROR;
300
301 auto ptr = glMapBufferRange(m_ssbo_pool_[ssboBufferID].second, 0, m_ssbo_pool_[ssboBufferID].first, GL_MAP_READ_BIT);
302 OPENGL_CHECK_ERROR;
303
304 if (ptr != nullptr) {
305 ::memcpy(ptr, hostData, size);
306 }
307
308 glUnmapBuffer(m_ssbo_pool_[ssboBufferID].second);
309 OPENGL_CHECK_ERROR;
310 return true;
311 }
312
GLCreateTexture(int w,int h,int c,GLenum TextrueFormat,GLenum target)313 GLuint OpenGLRuntime::GLCreateTexture(int w, int h, int c, GLenum TextrueFormat, GLenum target) {
314 GLuint textureID = 0;
315 if (target == GL_TEXTURE_3D) {
316 MS_ASSERT(w > 0 && h > 0 && c > 0);
317 glGenTextures(1, &textureID);
318 OPENGL_CHECK_ERROR;
319 glBindTexture(target, textureID);
320 OPENGL_CHECK_ERROR;
321 glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
322 OPENGL_CHECK_ERROR;
323 glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
324 OPENGL_CHECK_ERROR;
325 glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
326 OPENGL_CHECK_ERROR;
327 glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
328 OPENGL_CHECK_ERROR;
329 glTexParameteri(target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
330 OPENGL_CHECK_ERROR;
331
332 int realW = w;
333 int realH = h;
334 int realD = UP_DIV(c, kC4Align);
335 glTexStorage3D(target, 1, TextrueFormat, realW, realH, realD);
336 OPENGL_CHECK_ERROR;
337 } else if (target == GL_TEXTURE_2D) {
338 MS_ASSERT(w > 0 && h > 0);
339 glGenTextures(1, &textureID);
340 OPENGL_CHECK_ERROR;
341 glBindTexture(target, textureID);
342 OPENGL_CHECK_ERROR;
343 glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
344 OPENGL_CHECK_ERROR;
345 glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
346 OPENGL_CHECK_ERROR;
347 glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
348 OPENGL_CHECK_ERROR;
349 glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
350 OPENGL_CHECK_ERROR;
351 glTexParameteri(target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
352 OPENGL_CHECK_ERROR;
353
354 int realW = w * UP_DIV(c, kC4Align);
355 int realH = h;
356 glTexStorage2D(target, 1, TextrueFormat, realW, realH);
357 OPENGL_CHECK_ERROR;
358 }
359
360 std::vector<int> dims = {w, h, c};
361 std::vector<GLenum> props = {TextrueFormat, target};
362 m_texture_pool_[textureID] = std::make_pair(dims, props);
363
364 return textureID;
365 }
366
CopyDeviceTextureToSSBO(GLuint textureID,GLuint ssboBufferID)367 bool OpenGLRuntime::CopyDeviceTextureToSSBO(GLuint textureID, GLuint ssboBufferID) {
368 if (m_texture_pool_.find(textureID) == m_texture_pool_.end()) {
369 return false;
370 }
371 GLuint computeProgram;
372 if (m_texture_pool_[textureID].second[1] == GL_TEXTURE_2D) {
373 computeProgram = OpenGLRuntime::CreateComputeProgram(g_glsl_device_to_host_2d);
374 } else {
375 computeProgram = OpenGLRuntime::CreateComputeProgram(g_glsl_device_to_host_3d);
376 }
377 glUseProgram(computeProgram);
378
379 // bind the src image texture
380 glBindImageTexture(BIND_INDEX_0, textureID, 0, GL_TRUE, 0, GL_READ_ONLY, GL_RGBA32F);
381
382 // bind the dest output data
383 glBindBufferBase(GL_SHADER_STORAGE_BUFFER, BIND_INDEX_1, ssboBufferID);
384
385 // set uniform values
386 int width = m_texture_pool_[textureID].first[kWidthIndex];
387 int height = m_texture_pool_[textureID].first[kHeightIndex];
388 int channel = m_texture_pool_[textureID].first[kChannelIndex];
389
390 glUniform1i(BIND_INDEX_2, width);
391 glUniform1i(BIND_INDEX_3, height);
392 glUniform1i(BIND_INDEX_4, channel);
393
394 int c_4 = UP_DIV(channel, kC4Align);
395 int gLocalSize[3] = {4, 4, 1};
396 glDispatchCompute(UP_DIV(width, gLocalSize[FIRST_INPUT]), UP_DIV(height, gLocalSize[SECOND_INPUT]),
397 UP_DIV(c_4, gLocalSize[THIRD_INPUT]));
398
399 // memory sync
400 glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
401 glDeleteProgram(computeProgram);
402 return true;
403 }
404
CopyDeviceSSBOToTexture(GLuint ssboBufferID,GLuint textureID)405 bool OpenGLRuntime::CopyDeviceSSBOToTexture(GLuint ssboBufferID, GLuint textureID) {
406 if (m_texture_pool_.find(textureID) == m_texture_pool_.end()) {
407 return false;
408 }
409 GLuint computeProgram;
410 if (m_texture_pool_[textureID].second[1] == GL_TEXTURE_2D) {
411 computeProgram = OpenGLRuntime::CreateComputeProgram(g_glsl_host_to_device_2d);
412 } else {
413 computeProgram = OpenGLRuntime::CreateComputeProgram(g_glsl_host_to_device_3d);
414 }
415 glUseProgram(computeProgram);
416
417 // bind the src image texture
418 glBindImageTexture(BIND_INDEX_0, textureID, 0, GL_TRUE, 0, GL_WRITE_ONLY, GL_RGBA32F);
419
420 // bind the dest output data
421 glBindBufferBase(GL_SHADER_STORAGE_BUFFER, BIND_INDEX_1, ssboBufferID);
422
423 // set uniform values
424 int width = m_texture_pool_[textureID].first[kWidthIndex];
425 int height = m_texture_pool_[textureID].first[kHeightIndex];
426 int channel = m_texture_pool_[textureID].first[kChannelIndex];
427
428 glUniform1i(BIND_INDEX_2, width);
429 glUniform1i(BIND_INDEX_3, height);
430 glUniform1i(BIND_INDEX_4, channel);
431
432 int c_4 = UP_DIV(channel, kC4Align);
433 int gLocalSize[3] = {4, 4, 1};
434 glDispatchCompute(UP_DIV(width, gLocalSize[FIRST_INPUT]), UP_DIV(height, gLocalSize[SECOND_INPUT]),
435 UP_DIV(c_4, gLocalSize[THIRD_INPUT]));
436 // memory sync
437 glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
438 glDeleteProgram(computeProgram);
439 return true;
440 }
441
CopyHostToDeviceTexture(void * hostData,int width,int height,int channel)442 GLuint OpenGLRuntime::CopyHostToDeviceTexture(void *hostData, int width, int height, int channel) {
443 auto ssboBufferID = GLCreateSSBO(sizeof(float) * width * height * channel, hostData);
444 auto textureID = GLCreateTexture(width, height, channel, GL_RGBA32F, GL_TEXTURE_2D);
445 if (textureID == 0) {
446 MS_LOG(ERROR) << "generate GlTexture failed";
447 }
448 CopyDeviceSSBOToTexture(ssboBufferID, textureID);
449 return textureID;
450 }
451
CopyDeviceTextureToHost(GLuint textureID)452 void *OpenGLRuntime::CopyDeviceTextureToHost(GLuint textureID) {
453 int width = m_texture_pool_[textureID].first[kWidthIndex];
454 int height = m_texture_pool_[textureID].first[kHeightIndex];
455 int channel = m_texture_pool_[textureID].first[kChannelIndex];
456
457 auto ssboBufferID = GLCreateSSBO(sizeof(float) * width * height * channel);
458 CopyDeviceTextureToSSBO(textureID, ssboBufferID);
459 void *output = malloc(sizeof(float) * width * height * channel);
460 if (output == nullptr) {
461 MS_LOG(ERROR) << "Malloc host data failed";
462 return nullptr;
463 }
464 CopyDeviceSSBOToHost(ssboBufferID, output, sizeof(float) * width * height * channel);
465 return output;
466 }
467
PrintImage2DData(float * data,int w,int h,int c)468 void OpenGLRuntime::PrintImage2DData(float *data, int w, int h, int c) {
469 for (int i = 0; i < h; i++) {
470 for (int j = 0; j < w; j++) {
471 for (int k = 0; k < c; k++) {
472 std::cout << data[w * c * i + c * j + k] << " ";
473 }
474 std::cout << " ";
475 }
476 }
477 std::cout << "data print finish!" << std::endl;
478 }
479 #else
480 bool OpenGLRuntime::Init() {
481 MS_LOG(ERROR) << "Init error, server benchmark don't support opengl";
482 return false;
483 }
484
485 GLuint OpenGLRuntime::GLCreateTexture(int w, int h, int c, GLenum TextrueFormat, GLenum target) { return 0; }
486 void *OpenGLRuntime::CopyDeviceTextureToHost(GLuint textureID) { return nullptr; }
487 GLuint OpenGLRuntime::CopyHostToDeviceTexture(void *hostData, int width, int height, int channel) { return 0; }
488
489 void OpenGLRuntime::PrintImage2DData(float *data, int w, int h, int c) {}
490 #endif
491 } // namespace OpenGL
492 } // namespace mindspore
493