From 95ccc1295241fdde215d2b6a0e73adfdf06ebcf4 Mon Sep 17 00:00:00 2001 From: zhixunt Date: Mon, 1 Jan 2018 21:59:07 -0500 Subject: [PATCH] remove glad, add temporary rpc opengl test --- .gitmodules | 4 - CMakeLists.txt | 3 +- Makefile | 15 +- glad | 1 - python/tvm/exec/rpc_proxy.py | 2 +- src/runtime/opengl/opengl_common.h | 178 +++++++++++++- src/runtime/opengl/opengl_device_api.cc | 309 ++++++++++++++---------- src/runtime/opengl/opengl_module.cc | 2 +- src/runtime/rpc/rpc_module.cc | 12 + src/runtime/rpc/rpc_session.cc | 31 +++ src/runtime/rpc/rpc_session.h | 1 + tests/web/websock_rpc_test_opengl.py | 16 ++ web/example_rpc.html | 4 + web/web_runtime.cc | 1 - 14 files changed, 417 insertions(+), 162 deletions(-) delete mode 160000 glad create mode 100644 tests/web/websock_rpc_test_opengl.py diff --git a/.gitmodules b/.gitmodules index fadaa6a73da48..3f0b222a86c68 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,7 +7,3 @@ [submodule "dlpack"] path = dlpack url = https://github.com/dmlc/dlpack -[submodule "glad"] - path = glad - url = https://github.com/Dav1dde/glad.git - branch = c diff --git a/CMakeLists.txt b/CMakeLists.txt index d07106fae279e..c639963062a27 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -142,9 +142,8 @@ if(USE_OPENGL) find_package(glfw3 QUIET REQUIRED) message(STATUS "Build with OpenGL support") include_directories(${OPENGL_INCLUDE_DIRS}) - include_directories(glad/include) list(APPEND TVM_RUNTIME_LINKER_LIBS ${OpenGL_LIBRARIES} glfw dl) - list(APPEND RUNTIME_SRCS ${RUNTIME_OPENGL_SRCS} glad/src/glad.c) + list(APPEND RUNTIME_SRCS ${RUNTIME_OPENGL_SRCS}) add_definitions(-DTVM_OPENGL_RUNTIME=1) else(USE_OPENGL) add_definitions(-DTVM_OPENGL_RUNTIME=0) diff --git a/Makefile b/Makefile index 1a94b65bd0f1c..7906f9894ed0e 100644 --- a/Makefile +++ b/Makefile @@ -25,15 +25,15 @@ UNAME_S := $(shell uname -s) # The flags LLVM_CFLAGS= -fno-rtti -DDMLC_ENABLE_RTTI=0 -DDMLC_USE_FOPEN64=0 LDFLAGS = -pthread -lm -ldl -INCLUDE_FLAGS = -Iinclude -I$(DLPACK_PATH)/include -I$(DMLC_CORE_PATH)/include -IHalideIR/src -Itopi/include -Iglad/include +INCLUDE_FLAGS = -Iinclude -I$(DLPACK_PATH)/include -I$(DMLC_CORE_PATH)/include -IHalideIR/src -Itopi/include CFLAGS = -std=c++11 -Wall -O2 $(INCLUDE_FLAGS) -fPIC FRAMEWORKS = OBJCFLAGS = -fno-objc-arc EMCC_FLAGS= -std=c++11 -DDMLC_LOG_STACK_TRACE=0\ - -Oz -s RESERVED_FUNCTION_POINTERS=2 -s MAIN_MODULE=1 -s NO_EXIT_RUNTIME=1\ + -O0 -s RESERVED_FUNCTION_POINTERS=2 -s MAIN_MODULE=1 -s NO_EXIT_RUNTIME=1\ -s EXTRA_EXPORTED_RUNTIME_METHODS="['cwrap','getValue','setValue','addFunction']"\ + -s USE_GLFW=3 -s USE_WEBGL2=1 -lglfw\ $(INCLUDE_FLAGS) - # llvm configuration ifdef LLVM_CONFIG LLVM_VERSION=$(shell $(LLVM_CONFIG) --version| cut -b 1,3) @@ -55,7 +55,6 @@ CUDA_SRC = $(wildcard src/runtime/cuda/*.cc) ROCM_SRC = $(wildcard src/runtime/rocm/*.cc) OPENCL_SRC = $(wildcard src/runtime/opencl/*.cc) OPENGL_SRC = $(wildcard src/runtime/opengl/*.cc) -GLAD_SRC = glad/src/glad.c RPC_SRC = $(wildcard src/runtime/rpc/*.cc) GRAPH_SRC = $(wildcard src/runtime/graph/*.cc) RUNTIME_SRC = $(wildcard src/runtime/*.cc) @@ -68,7 +67,6 @@ CUDA_OBJ = $(patsubst src/%.cc, build/%.o, $(CUDA_SRC)) ROCM_OBJ = $(patsubst src/%.cc, build/%.o, $(ROCM_SRC)) OPENCL_OBJ = $(patsubst src/%.cc, build/%.o, $(OPENCL_SRC)) OPENGL_OBJ = $(patsubst src/%.cc, build/%.o, $(OPENGL_SRC)) -GLAD_OBJ = $(patsubst glad/src/%.c, build/%.o, $(GLAD_SRC)) RPC_OBJ = $(patsubst src/%.cc, build/%.o, $(RPC_SRC)) GRAPH_OBJ = $(patsubst src/%.cc, build/%.o, $(GRAPH_SRC)) CC_OBJ = $(patsubst src/%.cc, build/%.o, $(CC_SRC)) $(LLVM_OBJ) @@ -121,13 +119,13 @@ endif ifeq ($(USE_OPENGL), 1) CFLAGS += -DTVM_OPENGL_RUNTIME=1 + EMCC_FLAGS += -DTVM_OPENGL_RUNTIME=1 ifeq ($(UNAME_S), Darwin) FRAMEWORKS += -framework OpenGL else LDFLAGS += -lGL -lglfw endif RUNTIME_DEP += $(OPENGL_OBJ) - RUNTIME_DEP += $(GLAD_OBJ) else CFLAGS += -DTVM_OPENGL_RUNTIME=0 endif @@ -213,11 +211,6 @@ build/runtime/metal/%.o: src/runtime/metal/%.mm $(CXX) $(OBJCFLAGS) $(CFLAGS) -MM -MT build/runtime/metal/$*.o $< >build/runtime/metal/$*.d $(CXX) $(OBJCFLAGS) -c $(CFLAGS) -c $< -o $@ -build/glad.o: glad/src/glad.c - @mkdir -p $(@D) - $(CXX) $(CFLAGS) -MM -MT build/glad.o $< >build/glad.d - $(CXX) -c $(CFLAGS) -c glad/src/glad.c -o build/glad.o - build/%.o: src/%.cc @mkdir -p $(@D) $(CXX) $(CFLAGS) -MM -MT build/$*.o $< >build/$*.d diff --git a/glad b/glad deleted file mode 160000 index 5bf3eda6da606..0000000000000 --- a/glad +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5bf3eda6da606324999775b88a90ed572202be93 diff --git a/python/tvm/exec/rpc_proxy.py b/python/tvm/exec/rpc_proxy.py index 52fbda818596f..1539cbc01c3e5 100644 --- a/python/tvm/exec/rpc_proxy.py +++ b/python/tvm/exec/rpc_proxy.py @@ -14,7 +14,7 @@ def find_example_resource(): js_files = [ os.path.join(base_path, "web/tvm_runtime.js"), os.path.join(base_path, "lib/libtvm_web_runtime.js"), - os.path.join(base_path, "lib/libtvm_web_runtime.js.mem") + # os.path.join(base_path, "lib/libtvm_web_runtime.js.mem") ] for fname in [index_page] + js_files: if not os.path.exists(fname): diff --git a/src/runtime/opengl/opengl_common.h b/src/runtime/opengl/opengl_common.h index 25ab0692cbdeb..4ef0d9286d874 100644 --- a/src/runtime/opengl/opengl_common.h +++ b/src/runtime/opengl/opengl_common.h @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include #include @@ -22,10 +22,134 @@ namespace runtime { namespace gl { // This file contains the following classes. +class GLFunctionPointers; class OpenGLWorkspace; class Texture; class Program; +inline GLFWglproc GetProcAddress(const char *procname) { + GLFWglproc proc = glfwGetProcAddress(procname); + CHECK(proc != nullptr) << "Cannot get function pointer " << procname; + return proc; +} + +#define SetGLFunctionPointer(NAME) \ + NAME(decltype(NAME)(GetProcAddress("gl" #NAME))) + +/*! + * \brief The function pointers of all OpenGL APIs that are used. + * Must be constructed after creating an OpenGL context. + */ +class GLFunctionPointers { + public: + GLFunctionPointers() + : SetGLFunctionPointer(ActiveTexture), + SetGLFunctionPointer(AttachShader), + SetGLFunctionPointer(BindBuffer), + SetGLFunctionPointer(BindFramebuffer), + SetGLFunctionPointer(BindTexture), + SetGLFunctionPointer(BindVertexArray), + SetGLFunctionPointer(BufferData), + SetGLFunctionPointer(CheckFramebufferStatus), + SetGLFunctionPointer(Clear), + SetGLFunctionPointer(CompileShader), + SetGLFunctionPointer(CreateProgram), + SetGLFunctionPointer(CreateShader), + SetGLFunctionPointer(DeleteFramebuffers), + SetGLFunctionPointer(DeleteProgram), + SetGLFunctionPointer(DeleteShader), + SetGLFunctionPointer(DeleteTextures), + SetGLFunctionPointer(DetachShader), + SetGLFunctionPointer(DrawArrays), + SetGLFunctionPointer(DrawBuffers), + SetGLFunctionPointer(EnableVertexAttribArray), + SetGLFunctionPointer(Finish), + SetGLFunctionPointer(FramebufferTexture2D), + SetGLFunctionPointer(GenBuffers), + SetGLFunctionPointer(GenFramebuffers), + SetGLFunctionPointer(GenTextures), + SetGLFunctionPointer(GenVertexArrays), + SetGLFunctionPointer(GetAttribLocation), + SetGLFunctionPointer(GetError), + SetGLFunctionPointer(GetIntegerv), + SetGLFunctionPointer(GetProgramInfoLog), + SetGLFunctionPointer(GetProgramiv), + SetGLFunctionPointer(GetShaderInfoLog), + SetGLFunctionPointer(GetShaderiv), + SetGLFunctionPointer(GetString), + SetGLFunctionPointer(GetUniformLocation), + SetGLFunctionPointer(LinkProgram), + SetGLFunctionPointer(ReadPixels), + SetGLFunctionPointer(ShaderSource), + SetGLFunctionPointer(TexImage2D), + SetGLFunctionPointer(TexParameteri), + SetGLFunctionPointer(TexSubImage2D), + SetGLFunctionPointer(Uniform1i), + SetGLFunctionPointer(UseProgram), + SetGLFunctionPointer(VertexAttribPointer), + SetGLFunctionPointer(Viewport) { + LOG(INFO) << "Constructed GLFunctionPointers"; + } + + void (*ActiveTexture)(GLenum texture); + void (*AttachShader)(GLuint program, GLuint shader); + void (*BindBuffer)(GLenum target, GLuint buffer); + void (*BindFramebuffer)(GLenum target, GLuint framebuffer); + void (*BindTexture)(GLenum target, GLuint texture); + void (*BindVertexArray)(GLuint array); + void (*BufferData)(GLenum target, GLsizeiptr size, const GLvoid *data, + GLenum usage); + GLenum (*CheckFramebufferStatus)(GLenum target); + void (*Clear)(GLbitfield mask); + void (*CompileShader)(GLuint shader); + GLuint (*CreateProgram)(); + GLuint (*CreateShader)(GLenum shader_type); + void (*DeleteFramebuffers)(GLsizei n, const GLuint *framebuffers); + void (*DeleteProgram)(GLuint program); + void (*DeleteShader)(GLuint shader); + void (*DeleteTextures)(GLsizei n, const GLuint *textures); + void (*DetachShader)(GLuint program, GLuint shader); + void (*DrawArrays)(GLenum mode, GLint first, GLsizei count); + void (*DrawBuffers)(GLsizei n, const GLenum *bufs); + void (*EnableVertexAttribArray)(GLuint index); + void (*Finish)(); + void (*FramebufferTexture2D)(GLenum target, GLenum attachment, + GLenum textarget,GLuint texture, GLint level); + void (*GenBuffers)(GLsizei n, GLuint *buffers); + void (*GenFramebuffers)(GLsizei n, GLuint *ids); + void (*GenTextures)(GLsizei n, GLuint * textures); + void (*GenVertexArrays)(GLsizei n, GLuint *arrays); + GLint (*GetAttribLocation)(GLuint program, const GLchar *name); + GLenum (*GetError)(); + void (*GetIntegerv)(GLenum pname, GLint * data); + void (*GetProgramInfoLog)(GLuint program, GLsizei maxLength, GLsizei *length, + GLchar *infoLog); + void (*GetProgramiv)(GLuint program, GLenum pname, GLint *params); + void (*GetShaderInfoLog)(GLuint shader, GLsizei max_length, GLsizei *length, + GLchar *info_log); + void (*GetShaderiv)(GLuint shader, GLenum pname, GLint *params); + const GLubyte *(*GetString)(GLenum name); + GLint (*GetUniformLocation)(GLuint program, const GLchar *name); + void (*LinkProgram)(GLuint program); + void (*ReadPixels)(GLint x, GLint y, GLsizei width, GLsizei height, + GLenum format, GLenum type, GLvoid* data); + void (*ShaderSource)(GLuint shader, GLsizei count, const GLchar **string, + const GLint *length); + void (*TexImage2D)(GLenum target, GLint level, GLint internal_format, + GLsizei width, GLsizei height, GLint border, GLenum format, + GLenum type, const GLvoid *data); + void (*TexParameteri)(GLenum target, GLenum pname, GLint param); + void (*TexSubImage2D)(GLenum target, GLint level, GLint xoffset, + GLint yoffset, GLsizei width, GLsizei height, + GLenum format, GLenum type, const GLvoid* data); + void (*Uniform1i)(GLint location, GLint v0); + void (*UseProgram)(GLuint program); + void (*VertexAttribPointer)(GLuint index, GLint size, GLenum type, + GLboolean normalized, GLsizei stride, + const GLvoid * pointer); + void (*Viewport)(GLint x, GLint y, GLsizei width, GLsizei height); +}; + /*! * \brief Process global OpenGL workspace. */ @@ -57,6 +181,13 @@ class OpenGLWorkspace final : public DeviceAPI { */ std::unique_ptr CreateProgram(const char* fragment_shader_src); + std::unique_ptr CreateTexture(size_t nbytes); + + void PutTextureData(Texture *texture, GLint begin, GLsizei nelems, + const GLvoid* data); + + void GetTextureData(const Texture *texture, GLvoid* data); + /*! * \brief Get the global OpenGL workspace. * \return The global OpenGL workspace. @@ -76,10 +207,12 @@ class OpenGLWorkspace final : public DeviceAPI { private: friend class Texture; + friend class Program; OpenGLWorkspace(); GLFWwindow* window_; + std::unique_ptr gl; GLuint vertex_shader_; static const int kWindowWidth = 640; static const int kWindowHeight = 480; @@ -92,6 +225,12 @@ class OpenGLWorkspace final : public DeviceAPI { void BindTextureUnit(GLuint unit, GLuint texture); + void OnDeleteTexture(GLuint texture); + + void OnDeleteProgram(GLuint program); + + void CheckOpenGLError(); + GLuint NumTextureUnits(); /*! @@ -121,27 +260,35 @@ class OpenGLWorkspace final : public DeviceAPI { class Program { public: // Move constructor. - Program(Program&& other) noexcept : program_(other.program_) { + Program(Program&& other) noexcept + : workspace_(other.workspace_), program_(other.program_) { other.program_ = kInvalidProgram; } Program(const Program& other) = delete; Program& operator=(const Program& other) = delete; - ~Program(); + ~Program() { + if (program_ != kInvalidProgram) { + workspace_->OnDeleteProgram(program_); + program_ = kInvalidProgram; + } + } private: friend class OpenGLWorkspace; // Only OpenGLWorkspace can create a Program. // We enforce this to make sure OpenGL is initialized. - explicit Program(GLuint program) : program_(program) {} + explicit Program(OpenGLWorkspace* workspace, GLuint program) + : workspace_(workspace), program_(program) {} // The internal OpenGL program ID. GLuint program() { return program_; } static constexpr GLuint kInvalidProgram = static_cast(-1); + OpenGLWorkspace* workspace_; GLuint program_; }; @@ -154,35 +301,44 @@ class Texture { public: // Move constructor. Texture(Texture&& other) noexcept - : texture_(other.texture_), width_(other.width_), height_(other.height_) { + : workspace_(other.workspace_), texture_(other.texture_), + width_(other.width_), height_(other.height_) { other.texture_ = kInvalidTexture; } Texture(const Texture& other) = delete; Texture& operator=(const Texture& other) = delete; - ~Texture(); + ~Texture() { + if (texture_ != kInvalidTexture) { + LOG(INFO) << "Deleting texture [" << texture_ << "]"; + workspace_->OnDeleteTexture(texture_); + texture_ = kInvalidTexture; + } + } GLsizei width() const { return width_; } GLsizei height() const { return height_; } - void GetData(GLvoid* data) const; - - void PutData(GLint begin, GLsizei nelems, const GLvoid* data); - private: friend class OpenGLWorkspace; // Only OpenGLWorkspace can create a Texture. // We enforce this to make sure OpenGL is initialized. - explicit Texture(size_t nbytes); + // Always only use the first dimension of a 2D texture. + // The reason of using 2D textures is that texelFetch only supports 2D textures. + explicit Texture(OpenGLWorkspace* workspace, GLuint texture, GLsizei width, + GLsizei height) + : workspace_(workspace), texture_(texture), width_(width), + height_(height) {} // The internal texture ID. GLuint texture() const { return texture_; } static constexpr GLuint kInvalidTexture = static_cast(-1); + OpenGLWorkspace* workspace_; GLuint texture_; GLsizei width_; GLsizei height_; diff --git a/src/runtime/opengl/opengl_device_api.cc b/src/runtime/opengl/opengl_device_api.cc index 6ac664c0bf589..105362652e4fd 100644 --- a/src/runtime/opengl/opengl_device_api.cc +++ b/src/runtime/opengl/opengl_device_api.cc @@ -33,8 +33,8 @@ inline const char* GLGetErrorString(GLenum error) { } } -void OPENGL_CHECK_ERROR() { - GLenum err = glGetError(); +void OpenGLWorkspace::CheckOpenGLError() { + GLenum err = gl->GetError(); CHECK(err == GL_NO_ERROR) << "OpenGL error, code=" << err << ": " << gl::GLGetErrorString(err); } @@ -46,84 +46,13 @@ void OPENGL_CHECK_ERROR() { #define OPENGL_CALL(func) \ { \ (func); \ - OPENGL_CHECK_ERROR(); \ + CheckOpenGLError(); \ } void GlfwErrorCallback(int err, const char* str) { LOG(ERROR) << "Error: [" << err << "] " << str; } -// Always only use the first dimension of a 2D texture. -// The reason of using 2D textures is that texelFetch only supports 2D textures. -Texture::Texture(size_t nbytes) - : texture_(kInvalidTexture), - width_(static_cast(nbytes / sizeof(GLfloat))), - height_(1) { - LOG(INFO) << "Created texture [" << texture_ << "]"; - CHECK((nbytes % sizeof(GLfloat)) == 0) << "Must be multiple of GLfloats"; - - // Create a texture. - OPENGL_CALL(glGenTextures(1, &texture_)); - - auto workspace = gl::OpenGLWorkspace::Global(); - workspace->BindTextureUnit(workspace->NumTextureUnits() - 1, texture_); - - // Use glTexImage2D with nullptr data to specify GPU data storage. - // TODO(pengw): How can we know the type of data? - OPENGL_CALL(glTexImage2D(GL_TEXTURE_2D, /*level=*/0, GL_R32F, - width_, /*height=*/1, /*border=*/0, - GL_RED, GL_FLOAT, /*data=*/nullptr)); - - // TODO(zhixunt): What are these? - OPENGL_CALL( - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); - OPENGL_CALL( - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); - OPENGL_CALL( - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); - OPENGL_CALL( - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); -} - -Texture::~Texture() { - if (texture_ != kInvalidTexture) { - LOG(INFO) << "Deleting texture [" << texture_ << "]"; - - OPENGL_CALL(glDeleteTextures(1, &texture_)); - texture_ = kInvalidTexture; - } -} - -void Texture::GetData(GLvoid* data) const { - // Bind to temporary unit. - auto workspace = gl::OpenGLWorkspace::Global(); - workspace->BindTextureUnit(workspace->NumTextureUnits() - 1, this->texture_); - - glGetTexImage(GL_TEXTURE_2D, /*level=*/0, GL_RED, GL_FLOAT, data); -} - -void Texture::PutData(GLint begin, GLsizei nelems, const GLvoid* data) { - LOG(INFO) << "Texture::PutData(" << "begin = " << begin << ", " - << "nelems = " << nelems << ", data)"; - - // Bind to temporary unit. - auto workspace = gl::OpenGLWorkspace::Global(); - workspace->BindTextureUnit(workspace->NumTextureUnits() - 1, this->texture_); - - // Similar to cudaMemcpy. - OPENGL_CALL(glTexSubImage2D(GL_TEXTURE_2D, /*level=*/0, - /*xoffset=*/begin, /*yoffset=*/0, - /*width=*/nelems, /*height=*/1, - GL_RED, GL_FLOAT, data)); -} - -Program::~Program() { - if (program_ != kInvalidProgram) { - glDeleteProgram(program_); - program_ = kInvalidProgram; - } -} - const std::shared_ptr& OpenGLWorkspace::Global() { static std::shared_ptr inst(new OpenGLWorkspace); return inst; @@ -145,7 +74,8 @@ void* OpenGLWorkspace::AllocDataSpace( LOG(INFO) << "OpenGLWorkspace::AllocDataSpace(ctx, nbytes = " << nbytes << ", alignment = " << alignment << ")"; - return reinterpret_cast(new Texture(nbytes)); + auto texture = CreateTexture(nbytes).release(); + return reinterpret_cast(texture); } void OpenGLWorkspace::FreeDataSpace(TVMContext ctx, void* ptr) { @@ -195,7 +125,7 @@ void OpenGLWorkspace::CopyDataFromTo(const void* from, size == static_cast(texture->width()) * sizeof(GLfloat)) << "Only support full texture retrieval."; - texture->GetData(static_cast(to) + to_offset); + GetTextureData(texture, static_cast(to) + to_offset); } else if (ctx_from.device_type == kDLCPU && ctx_to.device_type == gl_devtype) { @@ -208,7 +138,7 @@ void OpenGLWorkspace::CopyDataFromTo(const void* from, const void* data = static_cast(from) + from_offset; auto begin = static_cast(to_offset / sizeof(GLfloat)); auto nelems = static_cast(size / sizeof(GLfloat)); - texture->PutData(begin, nelems, data); + PutTextureData(texture, begin, nelems, data); } else { LOG(FATAL) << "Expect copy from/to OpenGL or between OpenGL"; @@ -264,27 +194,32 @@ OpenGLWorkspace::OpenGLWorkspace() { // Before using any OpenGL API, we must specify a context. glfwMakeContextCurrent(window_); + LOG(INFO) << "Calling gladLoadGL..."; + + gl = std::unique_ptr(new GLFunctionPointers); + // Must be called after creating GLFW window. - gladLoadGL(); +// gladLoadGLLoader((GLADloadproc)glfwGetProcAddress); LOG(INFO) << "OpenGL says version: " << glGetString(GL_VERSION); - OPENGL_CHECK_ERROR(); + CheckOpenGLError(); // We always render the same vertices and triangles. GLuint vertex_buffer; - OPENGL_CALL(glGenBuffers(1, &vertex_buffer)); - OPENGL_CALL(glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer)); - OPENGL_CALL(glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, - GL_STATIC_DRAW)); + OPENGL_CALL(gl->GenBuffers(1, &vertex_buffer)); + OPENGL_CALL(gl->BindBuffer(GL_ARRAY_BUFFER, vertex_buffer)); + OPENGL_CALL(gl->BufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, + GL_STATIC_DRAW)); GLuint vertex_array; - OPENGL_CALL(glGenVertexArrays(1, &vertex_array)); - OPENGL_CALL(glBindVertexArray(vertex_array)); - glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer); + OPENGL_CALL(gl->GenVertexArrays(1, &vertex_array)); + OPENGL_CALL(gl->BindVertexArray(vertex_array)); + OPENGL_CALL(gl->BindBuffer(GL_ARRAY_BUFFER, vertex_buffer)); // We always use the same vertex shader. vertex_shader_ = CreateShader(GL_VERTEX_SHADER, vertex_shader_text_); + LOG(INFO) << "Created vertex shader"; } OpenGLWorkspace::~OpenGLWorkspace() { @@ -296,13 +231,21 @@ OpenGLWorkspace::~OpenGLWorkspace() { } void OpenGLWorkspace::BindTextureUnit(GLuint unit, GLuint texture) { - OPENGL_CALL(glActiveTexture(GL_TEXTURE0 + unit)); - OPENGL_CALL(glBindTexture(GL_TEXTURE_2D, texture)); + OPENGL_CALL(gl->ActiveTexture(GL_TEXTURE0 + unit)); + OPENGL_CALL(gl->BindTexture(GL_TEXTURE_2D, texture)); +} + +void OpenGLWorkspace::OnDeleteTexture(GLuint texture) { + OPENGL_CALL(gl->DeleteTextures(1, &texture)); +} + +void OpenGLWorkspace::OnDeleteProgram(GLuint program) { + OPENGL_CALL(gl->DeleteProgram(program)); } GLuint OpenGLWorkspace::NumTextureUnits() { GLint num_units; - OPENGL_CALL(glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &num_units)); + OPENGL_CALL(gl->GetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &num_units)); return static_cast(num_units); } @@ -318,7 +261,7 @@ const OpenGLWorkspace::Vertex OpenGLWorkspace::vertices[OpenGLWorkspace::kNumVer // Don't need to change this. // The vertex shader only needs to take in the triangle points. // No need for point transformations. -const char* OpenGLWorkspace::vertex_shader_text_ = "#version 330 core\n" +const char* OpenGLWorkspace::vertex_shader_text_ = "#version 300 es\n" "in vec2 point; // input to vertex shader\n" "void main() {\n" " gl_Position = vec4(point, 0.0, 1.0);\n" @@ -333,7 +276,7 @@ std::unique_ptr OpenGLWorkspace::CreateProgram( // Link the shaders and create the program. auto program = CreateProgram(fragment_shader); - OPENGL_CALL(glDeleteShader(fragment_shader)); + OPENGL_CALL(gl->DeleteShader(fragment_shader)); return program; } @@ -341,63 +284,135 @@ std::unique_ptr OpenGLWorkspace::CreateProgram( GLuint OpenGLWorkspace::CreateShader(GLenum shader_kind, const char* shader_src) { // Create the shader. - GLuint shader = glCreateShader(shader_kind); - glShaderSource(shader, 1, &shader_src, nullptr); - glCompileShader(shader); + GLuint shader = gl->CreateShader(shader_kind); + gl->ShaderSource(shader, 1, &shader_src, nullptr); + gl->CompileShader(shader); // Check compile errors. GLint err; - glGetShaderiv(shader, GL_COMPILE_STATUS, &err); + gl->GetShaderiv(shader, GL_COMPILE_STATUS, &err); GLint info_log_len; - glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_log_len); + gl->GetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_log_len); - if (info_log_len > 0) { + if (err != GL_TRUE) { std::unique_ptr err_msg(new char[info_log_len + 1]); - glGetShaderInfoLog(shader, info_log_len, nullptr, err_msg.get()); + gl->GetShaderInfoLog(shader, info_log_len, nullptr, err_msg.get()); LOG(ERROR) << err_msg.get(); assert(false); } - OPENGL_CHECK_ERROR(); + CheckOpenGLError(); return shader; } +std::unique_ptr OpenGLWorkspace::CreateTexture(size_t nbytes) { + CHECK((nbytes % sizeof(GLfloat)) == 0) << "Must be multiple of GLfloats"; + + // Create a texture. + GLuint texture; + OPENGL_CALL(gl->GenTextures(1, &texture)); + + BindTextureUnit(NumTextureUnits() - 1, texture); + + // Use glTexImage2D with nullptr data to specify GPU data storage. + // TODO(pengw): How can we know the type of data? + auto width = static_cast(nbytes / sizeof(GLfloat)); + auto height = GLsizei(1); + OPENGL_CALL(gl->TexImage2D(GL_TEXTURE_2D, /*level=*/0, GL_R32F, + width, height, /*border=*/0, + GL_RED, GL_FLOAT, /*data=*/nullptr)); + + // TODO(zhixunt): What are these? + OPENGL_CALL( + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + OPENGL_CALL( + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + OPENGL_CALL( + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); + OPENGL_CALL( + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); + + LOG(INFO) << "Created texture [" << texture << "]"; + + return std::unique_ptr(new Texture(this, texture, width, height)); +} + std::unique_ptr OpenGLWorkspace::CreateProgram( GLuint fragment_shader) { // Create the program and link the shaders. - GLuint program = glCreateProgram(); - glAttachShader(program, vertex_shader_); - glAttachShader(program, fragment_shader); - glLinkProgram(program); + GLuint program = gl->CreateProgram(); + gl->AttachShader(program, vertex_shader_); + gl->AttachShader(program, fragment_shader); + gl->LinkProgram(program); // Check link errors. GLint err; - glGetProgramiv(program, GL_LINK_STATUS, &err); + gl->GetProgramiv(program, GL_LINK_STATUS, &err); GLint info_log_len; - glGetProgramiv(program, GL_INFO_LOG_LENGTH, &info_log_len); + gl->GetProgramiv(program, GL_INFO_LOG_LENGTH, &info_log_len); - if (info_log_len > 0) { + if (err != GL_TRUE) { std::unique_ptr err_msg(new char[info_log_len + 1]); - glGetProgramInfoLog(program, info_log_len, nullptr, err_msg.get()); + gl->GetProgramInfoLog(program, info_log_len, nullptr, err_msg.get()); LOG(ERROR) << err_msg.get(); assert(false); } - OPENGL_CHECK_ERROR(); + CheckOpenGLError(); + + OPENGL_CALL(gl->DetachShader(program, vertex_shader_)); + OPENGL_CALL(gl->DetachShader(program, fragment_shader)); + + auto point_attrib = GLuint(gl->GetAttribLocation(program, "point")); + OPENGL_CALL(gl->EnableVertexAttribArray(point_attrib)); + + OPENGL_CALL(gl->VertexAttribPointer(point_attrib, 2, GL_FLOAT, GL_FALSE, + sizeof(Vertex), nullptr)); + + return std::unique_ptr(new Program(this, program)); +} + +void OpenGLWorkspace::PutTextureData(Texture *texture, GLint begin, + GLsizei nelems, const GLvoid* data) { + LOG(INFO) << "Texture::PutData(" << "begin = " << begin << ", " + << "nelems = " << nelems << ", data)"; + + // Bind to temporary unit. + BindTextureUnit(NumTextureUnits() - 1, texture->texture()); + + // Similar to cudaMemcpy. + OPENGL_CALL(gl->TexSubImage2D(GL_TEXTURE_2D, /*level=*/0, + /*xoffset=*/begin, /*yoffset=*/0, + /*width=*/nelems, /*height=*/1, + GL_RED, GL_FLOAT, data)); +} + +void OpenGLWorkspace::GetTextureData(const Texture *texture, GLvoid* data) { + BindTextureUnit(NumTextureUnits() - 1, texture->texture()); - OPENGL_CALL(glDetachShader(program, vertex_shader_)); - OPENGL_CALL(glDetachShader(program, fragment_shader)); + // Create frame buffer. + GLuint frame_buffer; + OPENGL_CALL(gl->GenFramebuffers(1, &frame_buffer)); + OPENGL_CALL(gl->BindFramebuffer(GL_FRAMEBUFFER, frame_buffer)); + + // Bind texture to framebuffer's attachment #0. + OPENGL_CALL(gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, texture->texture(), 0)); - auto point_attrib = GLuint(glGetAttribLocation(program, "point")); - OPENGL_CALL(glEnableVertexAttribArray(point_attrib)); + // Always check that our framebuffer is ok + if (gl->CheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + LOG(ERROR) << "Framebuffer not complete."; + assert(false); + } - OPENGL_CALL(glVertexAttribPointer(point_attrib, 2, GL_FLOAT, GL_FALSE, - sizeof(Vertex), nullptr)); + OPENGL_CALL(gl->ReadPixels(/*x=*/0, /*y=*/0, /*width=*/texture->width(), + /*height=*/texture->height(), GL_RED, GL_FLOAT, + data)); - return std::unique_ptr(new Program(program)); + OPENGL_CALL(gl->DeleteFramebuffers(1, &frame_buffer)); } void OpenGLWorkspace::Render( @@ -409,24 +424,24 @@ void OpenGLWorkspace::Render( assert(false); } - OPENGL_CALL(glUseProgram(program.program_)); + OPENGL_CALL(gl->UseProgram(program.program_)); // Create frame buffer. GLuint frame_buffer; - OPENGL_CALL(glGenFramebuffers(1, &frame_buffer)); - OPENGL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, frame_buffer)); + OPENGL_CALL(gl->GenFramebuffers(1, &frame_buffer)); + OPENGL_CALL(gl->BindFramebuffer(GL_FRAMEBUFFER, frame_buffer)); // Set "renderedTexture" as our colour attachement #0 - OPENGL_CALL(glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - output->texture(), 0)); + OPENGL_CALL(gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, output->texture(), 0)); // Set the list of draw buffers. GLenum DrawBuffers[1] = {GL_COLOR_ATTACHMENT0}; // "1" is the size of DrawBuffers. - OPENGL_CALL(glDrawBuffers(1, DrawBuffers)); + OPENGL_CALL(gl->DrawBuffers(1, DrawBuffers)); // Always check that our framebuffer is ok - if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + if (gl->CheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { LOG(ERROR) << "Framebuffer not complete."; assert(false); } @@ -438,18 +453,18 @@ void OpenGLWorkspace::Render( BindTextureUnit(unit, texture->texture()); - GLint texture_uniform = glGetUniformLocation(program.program_, - name.c_str()); - OPENGL_CALL(glUniform1i(texture_uniform, unit)); + GLint texture_uniform = gl->GetUniformLocation(program.program_, + name.c_str()); + OPENGL_CALL(gl->Uniform1i(texture_uniform, unit)); } - OPENGL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, frame_buffer)); - OPENGL_CALL(glViewport(0, 0, output->width(), output->height())); + OPENGL_CALL(gl->BindFramebuffer(GL_FRAMEBUFFER, frame_buffer)); + OPENGL_CALL(gl->Viewport(0, 0, output->width(), output->height())); - OPENGL_CALL(glClear(GL_COLOR_BUFFER_BIT)); - OPENGL_CALL(glDrawArrays(GL_TRIANGLES, 0, 6)); + OPENGL_CALL(gl->Clear(GL_COLOR_BUFFER_BIT)); + OPENGL_CALL(gl->DrawArrays(GL_TRIANGLES, 0, 6)); - glDeleteFramebuffers(1, &frame_buffer); + gl->DeleteFramebuffers(1, &frame_buffer); } TVM_REGISTER_GLOBAL("device_api.opengl") @@ -459,6 +474,40 @@ TVM_REGISTER_GLOBAL("device_api.opengl") }); } // namespace gl + +void TestLocalOpenGL() { + std::cout << "Calling TestLocalOpenGL()" << std::endl; + + std::shared_ptr workspace = gl::OpenGLWorkspace::Global(); + + std::cout << "Got workspace" << std::endl; + + const char* shader_src = "#version 300 es\n" + "precision highp float;\n" + "out float color;\n" + "void main() {\n" + " color = 0.0;\n" + "}\n"; + + std::unique_ptr program = workspace->CreateProgram(shader_src); + + std::cout << "Created program" << std::endl; + + std::unique_ptr output = workspace->CreateTexture(16); + + std::cout << "Created texture" << std::endl; + + workspace->Render(*program, {}, output.get()); + + std::cout << "Rendered" << std::endl; +} + +TVM_REGISTER_GLOBAL("contrib.rpc._TestLocalOpenGL") +.set_body([](TVMArgs args, TVMRetValue* rv) { + TestLocalOpenGL(); + *rv = nullptr; +}); + } // namespace runtime } // namespace tvm diff --git a/src/runtime/opengl/opengl_module.cc b/src/runtime/opengl/opengl_module.cc index 7603620c4907d..e0f102b10bb54 100644 --- a/src/runtime/opengl/opengl_module.cc +++ b/src/runtime/opengl/opengl_module.cc @@ -155,7 +155,7 @@ Module OpenGLModuleCreate(std::string data, std::string fmt, std::unordered_map fmap) { LOG(INFO) << "OpenGLModuleCreate() " << data << " " << fmt << " " - << fmap.size(); + << fmap.size(); auto n = std::make_shared(data, fmt, fmap); return Module(n); } diff --git a/src/runtime/rpc/rpc_module.cc b/src/runtime/rpc/rpc_module.cc index af2e0647871dc..7bbaf5fccbd64 100644 --- a/src/runtime/rpc/rpc_module.cc +++ b/src/runtime/rpc/rpc_module.cc @@ -7,6 +7,8 @@ #include #include #include "./rpc_session.h" +#include "../opengl/opengl_module.h" +#include "../opengl/opengl_common.h" namespace tvm { namespace runtime { @@ -155,6 +157,16 @@ TVM_REGISTER_GLOBAL("module._RPCTimeEvaluator") } }); +TVM_REGISTER_GLOBAL("contrib.rpc._TestRemoteOpenGL") +.set_body([](TVMArgs args, TVMRetValue* rv) { + Module m = args[0]; + std::string tkey = m->type_key(); + CHECK_EQ(tkey, "rpc"); + auto& sess = static_cast(m.operator->())->sess(); + sess->CallRemote(RPCCode::kTestRemoteOpenGL); + *rv = nullptr; + }); + TVM_REGISTER_GLOBAL("contrib.rpc._LoadRemoteModule") .set_body([](TVMArgs args, TVMRetValue* rv) { Module m = args[0]; diff --git a/src/runtime/rpc/rpc_session.cc b/src/runtime/rpc/rpc_session.cc index 0fa021918ed2c..69ed5232d0ff8 100644 --- a/src/runtime/rpc/rpc_session.cc +++ b/src/runtime/rpc/rpc_session.cc @@ -12,6 +12,8 @@ #include #include "./rpc_session.h" #include "../../common/ring_buffer.h" +#include "../opengl/opengl_module.h" +#include "../opengl/opengl_common.h" namespace tvm { namespace runtime { @@ -969,6 +971,34 @@ void RPCModuleGetSource(TVMArgs args, TVMRetValue *rv) { *rv = (*static_cast(mhandle))->GetSource(fmt); } +void RPCModuleTestRemoteOpenGL(TVMArgs args, TVMRetValue* rv) { + std::cout << "RPCModuleTestRemoteOpenGL" << std::endl; + + std::shared_ptr workspace = gl::OpenGLWorkspace::Global(); + std::cout << "Got workspace" << std::endl; + + const char* shader_src = "#version 300 es\n" + "precision highp float;\n" + "out float color;\n" + "void main() {\n" + " color = 0.0;\n" + "}\n"; + + std::unique_ptr program = workspace->CreateProgram(shader_src); + + std::cout << "Created program" << std::endl; + + std::unique_ptr output = workspace->CreateTexture(16); + + std::cout << "Created texture" << std::endl; + + workspace->Render(*program, {}, output.get()); + + std::cout << "Rendered" << std::endl; + + *rv = nullptr; +} + void RPCGetTimeEvaluator(TVMArgs args, TVMRetValue *rv) { PackedFunc *pf = static_cast(args[0].operator void*()); void *fhandle = new PackedFunc(WrapTimeEvaluator(*pf, args[1], args[2])); @@ -1017,6 +1047,7 @@ void RPCSession::EventHandler::HandlePackedCall() { case RPCCode::kModuleFree: CallHandler(RPCModuleFree); break; case RPCCode::kModuleGetFunc: CallHandler(RPCModuleGetFunc); break; case RPCCode::kModuleGetSource: CallHandler(RPCModuleGetSource); break; + case RPCCode::kTestRemoteOpenGL: CallHandler(RPCModuleTestRemoteOpenGL); break; default: LOG(FATAL) << "Unknown event " << static_cast(code_); } CHECK_EQ(state_, kRecvCode); diff --git a/src/runtime/rpc/rpc_session.h b/src/runtime/rpc/rpc_session.h index 80dde91714012..e2884062172dc 100644 --- a/src/runtime/rpc/rpc_session.h +++ b/src/runtime/rpc/rpc_session.h @@ -48,6 +48,7 @@ enum class RPCCode : int { kModuleFree, kModuleGetFunc, kModuleGetSource, + kTestRemoteOpenGL, }; /*! diff --git a/tests/web/websock_rpc_test_opengl.py b/tests/web/websock_rpc_test_opengl.py new file mode 100644 index 0000000000000..09e641716d3dc --- /dev/null +++ b/tests/web/websock_rpc_test_opengl.py @@ -0,0 +1,16 @@ +import tvm +from tvm.contrib import rpc + +proxy_host = "localhost" +proxy_port = 9090 + +def test_remote_opengl(): + rpc._TestLocalOpenGL() + print("_TestLocalOpenGL completed.") + + if not tvm.module.enabled("rpc"): + return + remote = rpc.connect(proxy_host, proxy_port, key="js") + rpc._TestRemoteOpenGL(remote._sess) + +test_remote_opengl() diff --git a/web/example_rpc.html b/web/example_rpc.html index bcccbef7358f7..b23ecda8e0179 100644 --- a/web/example_rpc.html +++ b/web/example_rpc.html @@ -36,5 +36,9 @@

Options

+ + diff --git a/web/web_runtime.cc b/web/web_runtime.cc index b080bb750bbe6..b8db8cce70062 100644 --- a/web/web_runtime.cc +++ b/web/web_runtime.cc @@ -20,7 +20,6 @@ #include "../src/runtime/graph/graph_runtime.cc" #include "../src/runtime/opengl/opengl_device_api.cc" #include "../src/runtime/opengl/opengl_module.cc" -#include "../glad/src/glad.c" namespace tvm { namespace contrib {