[OpenGL]使用 Compute Shader 实现矩阵点乘

一、简介

本文介绍了如何使用 OpenGL 中的 compute shader 进行矩阵相乘的并行运算。代码目标是,输入两个大小为 10*10 的矩阵 A 和 B,计算 A*B 的结果并存储到矩阵 C 中。

二、代码

0. 代码逻辑

1. 初始化 glfw, glad, 窗口
2. 初始化 compute shader
3. 准备输入数据
4. 运行 compute shader
5. 读取结果并打印
6. 释放资源

1. main.cpp

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include "ComputeShader.hpp"#include <cstdint>
#include <iostream>
#include <iostream>// 用于处理窗口大小改变的回调函数
void framebuffer_size_callback(GLFWwindow *window, int width, int height);
void window_close_callback(GLFWwindow *window);// 用于处理用户输入的函数
void processInput(GLFWwindow *window);// 指定窗口默认width和height像素大小
unsigned int SCR_WIDTH = 800;
unsigned int SCR_HEIGHT = 600;/************************************/int main()
{/****** 1. 初始化 glfw, glad, 窗口 *******/// glfw 初始化 + 配置 glfw 参数glfwInit();glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);// glfw 生成窗口GLFWwindow *window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);if (window == NULL){// 检查是否成功生成窗口,如果没有成功打印出错信息并且退出std::cout << "Failed to create GLFW window" << std::endl;glfwTerminate();return -1;}// 设置窗口window的上下文glfwMakeContextCurrent(window);// 配置window变化时的回调函数glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);// 设置窗口关闭回调glfwSetWindowCloseCallback(window, window_close_callback);// 使用 glad 加载 OpenGL 中的各种函数if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)){std::cout << "Failed to initialize GLAD" << std::endl;return -1;}/************************************//****** 2. 初始化 compute shader ******/ComputeShader computeShader("../resources/Compute.comp");/************************************//****** 3. 准备输入数据 ******/// 输入矩阵 Afloat A[100];for (int i = 0; i < 10; i++){for (int j = 0; j < 10; j++){A[i * 10 + j] = 1.0f * i;}}// 输入矩阵 Bfloat B[100];for (int i = 0; i < 10; i++){for (int j = 0; j < 10; j++){B[i * 10 + j] = 1.0f * i;}}// 输出矩阵 Cfloat C[100];GLuint SSBO_A, SSBO_B, SSBO_C;glGenBuffers(1, &SSBO_A);glGenBuffers(1, &SSBO_B);glGenBuffers(1, &SSBO_C);glBindBuffer(GL_SHADER_STORAGE_BUFFER, SSBO_A);glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(A), A, GL_STATIC_READ);glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, SSBO_A);glBindBuffer(GL_SHADER_STORAGE_BUFFER, SSBO_B);glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(B), B, GL_STATIC_READ);glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, SSBO_B);glBindBuffer(GL_SHADER_STORAGE_BUFFER, SSBO_C);glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(C), C, GL_DYNAMIC_DRAW);glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, SSBO_C);/************************************//****** 4. 运行 compute shader ******/// 运行 compute shader, 分为 10*10*1 个 workgroup, 每个 workgroup 计算 C 矩阵中的一个元素值computeShader.use();glDispatchCompute((unsigned int)10, (unsigned int)10, 1);glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);/************************************//****** 5. 读取结果并打印 ******/glBindBuffer(GL_SHADER_STORAGE_BUFFER, SSBO_C);glGetBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, sizeof(C), C);for (int row = 0; row < 10; ++row){for (int col = 0; col < 10; ++col){printf("%0.3f ", C[row * 10 + col]);}printf("\n");}/************************************//****** 6.释放资源 ******/// glfw 释放 glfw使用的所有资源glfwTerminate();/************************************/return 0;
}// 用于处理用户输入的函数
void processInput(GLFWwindow *window)
{// 当按下 Esc 按键时调用 glfwSetWindowShouldClose() 函数,关闭窗口if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS){glfwSetWindowShouldClose(window, true);}
}// 在使用 OpenGL 和 GLFW 库时,处理窗口大小改变的回调函数
// 当窗口大小发生变化时,确保 OpenGL 渲染的内容能够适应新的窗口大小,避免图像被拉伸、压缩或出现其他比例失真的问题
void framebuffer_size_callback(GLFWwindow *window, int width, int height)
{SCR_WIDTH = width;SCR_HEIGHT = height;glViewport(0, 0, width, height);
}
void window_close_callback(GLFWwindow *window)
{// 这里可以做一些额外的清理工作// 例如释放资源、记录日志等std::cout << "Window is closing..." << std::endl;
}

2. ComputeShader 类

#ifndef COMPUTESHADER_H
#define COMPUTESHADER_H#include <glad/glad.h>
#include <glm/glm.hpp>#include <string>
#include <fstream>
#include <sstream>
#include <iostream>class ComputeShader
{public:unsigned int ID;// constructor generates the shader on the fly// ------------------------------------------------------------------------ComputeShader() {};ComputeShader(const char *computePath){// 1. retrieve the vertex/fragment source code from filePathstd::string computeCode;std::ifstream cShaderFile;// ensure ifstream objects can throw exceptions:cShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);try{// open filescShaderFile.open(computePath);std::stringstream cShaderStream;// read file's buffer contents into streamscShaderStream << cShaderFile.rdbuf();// close file handlerscShaderFile.close();// convert stream into stringcomputeCode = cShaderStream.str();}catch (std::ifstream::failure &e){std::cout << "ERROR::SHADER::FILE_NOT_SUCCESSFULLY_READ: " << e.what() << std::endl;}const char *cShaderCode = computeCode.c_str();// 2. compile shadersunsigned int compute;// compute shadercompute = glCreateShader(GL_COMPUTE_SHADER);glShaderSource(compute, 1, &cShaderCode, NULL);glCompileShader(compute);checkCompileErrors(compute, "COMPUTE");// shader ProgramID = glCreateProgram();glAttachShader(ID, compute);glLinkProgram(ID);checkCompileErrors(ID, "PROGRAM");// delete the shaders as they're linked into our program now and no longer necessaryglDeleteShader(compute);}// activate the shader// ------------------------------------------------------------------------void use() const{glUseProgram(ID);}// ------------------------------------------------------------------------void setInt(const std::string &name, int value) const{glUniform1i(glGetUniformLocation(ID, name.c_str()), value);}private:// utility function for checking shader compilation/linking errors.// ------------------------------------------------------------------------void checkCompileErrors(GLuint shader, std::string type){GLint success;GLchar infoLog[1024];if (type != "PROGRAM"){glGetShaderiv(shader, GL_COMPILE_STATUS, &success);if (!success){glGetShaderInfoLog(shader, 1024, NULL, infoLog);std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: " << type << "\n"<< infoLog << "\n -- --------------------------------------------------- -- " << std::endl;}}else{glGetProgramiv(shader, GL_LINK_STATUS, &success);if (!success){glGetProgramInfoLog(shader, 1024, NULL, infoLog);std::cout << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "\n"<< infoLog << "\n -- --------------------------------------------------- -- " << std::endl;}}}
};
#endif

3. compute shader (Compute.comp)

#version 430layout(std430, binding = 0) buffer inputMatrixA { float A[]; };
layout(std430, binding = 1) buffer inputMatrixB { float B[]; };
layout(std430, binding = 2) buffer OnputData { float C[]; };layout(local_size_x = 1,local_size_y = 1) in; // 每个 workgroup item 计算 C 的一个元素void main() {//   获取当前 workgroup item 的全局位置uint row = gl_GlobalInvocationID.x;uint col = gl_GlobalInvocationID.y;// 确保不会越界if (row >= 10 || col >= 10) {return;}// 从矩阵 A 和矩阵 B 中读取数据float valueA = 0.0f;float valueB = 0.0f;// 计算矩阵 C 中对应的元素float result = 0.0;for (int k = 0; k < 10; k++) {valueA = A[row * 10 + k];valueB = B[k * 10 + col];result += valueA * valueB; // 矩阵乘法}C[row * 10 + col] = result;
}

4. 运行结果

0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 
45.000 45.000 45.000 45.000 45.000 45.000 45.000 45.000 45.000 45.000 
90.000 90.000 90.000 90.000 90.000 90.000 90.000 90.000 90.000 90.000 
135.000 135.000 135.000 135.000 135.000 135.000 135.000 135.000 135.000 135.000 
180.000 180.000 180.000 180.000 180.000 180.000 180.000 180.000 180.000 180.000 
225.000 225.000 225.000 225.000 225.000 225.000 225.000 225.000 225.000 225.000 
270.000 270.000 270.000 270.000 270.000 270.000 270.000 270.000 270.000 270.000 
315.000 315.000 315.000 315.000 315.000 315.000 315.000 315.000 315.000 315.000 
360.000 360.000 360.000 360.000 360.000 360.000 360.000 360.000 360.000 360.000 
405.000 405.000 405.000 405.000 405.000 405.000 405.000 405.000 405.000 405.000

三、参考

[1]LearnOpenGL-Guest Articles-2022-Compute Shaders

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/496742.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Unable to create schema compiler

问题 Unable to create schema compiler 原因分析 可能一 服务上只安装了jre&#xff0c;缺少需要的jar包 可能二 jdk的版本是9以上&#xff0c;默认不带这些jar包 解决办法 方案一&#xff08;亲测可用&#xff09; 上面的报错是在使用CXF框架生成动态客户端client时…

D类音频应用EMI管理

1、前言 对于EMI&#xff0c;首先需要理解天线。频率和波长之间的关系&#xff0c;如下图所示。   作为有效天线所需的最短长度是λ/4。在空气中&#xff0c;介电常数是1&#xff0c;但是在FR4或玻璃环氧PCB的情况下&#xff0c;介电常数大约4.8。这种效应会导致信号在FR4材…

CSES-1687 Company Queries I(倍增法)

题目传送门https://vjudge.net/problem/CSES-1687#authorGPT_zh 解题思路 其实和倍增法求 LCA 是一样的…… 首先设 表示 号点的上面的第 个祖先是谁。 同倍增法&#xff1a; 然后&#xff0c;题目要求我们向上跳 个点。 枚举 &#xff08;从大到小&#xff0c;想想为…

【从零开始入门unity游戏开发之——unity篇01】unity6基础入门开篇——游戏引擎是什么、主流的游戏引擎、为什么选择Unity

文章目录 前言**游戏引擎是什么&#xff1f;****游戏引擎对于我们的意义**1、**降低游戏开发的门槛**2、**提升游戏开发效率** **以前做游戏****现在做游戏****主流的游戏引擎有哪些&#xff1f;**Unity 相比其他游戏引擎的优势&#xff1f;**为什么选择Unity&#xff1f;**Uni…

Apifox 12月更新|接口的测试覆盖情况、测试场景支持修改记录、迭代分支能力升级、自定义项目角色权限、接口可评论

Apifox 新版本上线啦&#xff01;&#xff01;&#xff01; 在快速迭代的开发流程中&#xff0c;接口测试工具的强大功能往往决定了项目的效率和质量。而 Apifox 在 12 月的更新中&#xff0c;再次引领潮流&#xff0c;推出了一系列重磅功能&#xff01;测试覆盖情况分析、场景…

C# GDI+数码管数字控件

调用方法 int zhi 15;private void button1_Click(object sender, EventArgs e){if (zhi > 19){zhi 0;}lcdDisplayControl1.DisplayText zhi.ToString();} 运行效果 控件代码 using System; using System.Collections.Generic; using System.Drawing.Drawing2D; using …

WebRTC服务质量(12)- Pacer机制(04) 向Pacer中插入数据

WebRTC服务质量&#xff08;01&#xff09;- Qos概述 WebRTC服务质量&#xff08;02&#xff09;- RTP协议 WebRTC服务质量&#xff08;03&#xff09;- RTCP协议 WebRTC服务质量&#xff08;04&#xff09;- 重传机制&#xff08;01) RTX NACK概述 WebRTC服务质量&#xff08;…

C#实现调用DLL 套壳读卡程序(桌面程序开发)

背景 正常业务已经支持 读三代卡了&#xff0c;前端调用医保封装好的服务就可以了&#xff0c;但是长护要读卡&#xff0c;就需要去访问万达&#xff0c;他们又搞了一套读卡的动态库&#xff0c;为了能够掉万达的接口&#xff0c;就需要去想办法调用它们提供的动态库方法&…

低代码开发平台排名2024

低代码开发平台在过去几年中迅速崛起&#xff0c;成为企业数字化转型的重要工具。这些平台通过可视化界面和拖放组件&#xff0c;使业务人员和技术人员都能快速构建应用程序&#xff0c;大大缩短了开发周期。以下是一些在2024年值得关注和使用的低代码开发平台。 一、Zoho Cre…

rocketmq-push模式-消费侧重平衡-类流程图分析

1、观察consumer线程 使用arthas分析 MQClientFactoryScheduledThread 定时任务线程 定时任务线程&#xff0c;包含如下任务&#xff1a; 每2分钟更新nameServer列表 每30秒更新topic的路由信息 每30秒检查broker的存活&#xff0c;发送心跳请求 每5秒持久化消费队列的offset…

群落生态学研究进展▌Hmsc包对于群落生态学假说的解读、Hmsc包开展单物种和多物种分析的技术细节及Hmsc包的实际应用

HMSC&#xff08;Hierarchical Species Distribution Models&#xff09;是一种用于预测物种分布的统计模型。它在群落生态学中的应用广泛&#xff0c;可以帮助科学家研究物种在不同环境条件下的分布规律&#xff0c;以及预测物种在未来环境变化下的潜在分布范围。 举例来说&a…

影视仓最新接口+内置本包方法的研究(2024.12.27)

近日喜欢上了研究影视的本地仓库内置&#xff0c;也做了一个分享到了群里。 内置本地仓库包的好处很明显&#xff0c;当前线路接口都是依赖网络上的代码站存放&#xff0c;如果维护者删除那就GG。 虽然有高手制作了很多本地包&#xff0c;但推送本地包到APP&#xff0c;难倒一片…

教育元宇宙的优势与核心功能解析

随着科技的飞速发展&#xff0c;教育领域正迎来一场前所未有的变革。教育元宇宙作为新兴的教育形态&#xff0c;以其独特的优势和丰富的功能&#xff0c;正在逐步改变我们的学习方式。本文将深入探讨教育元宇宙的优势以及其核心功能&#xff0c;为您揭示这一未来教育的新趋势。…

多个微服务 Mybatis 过程中出现了Invalid bound statement (not found)的特殊问题

针对多个微服务的场景&#xff0c;记录一下这个特殊问题&#xff1a; 如果启动类上用了这个MapperScan注解 在resource 目录下必须建相同的 com.demo.biz.mapper 目录结构&#xff0c;否则会加载不到XML资源文件 。 并且切记是com/demo/biz 这样的格式创建&#xff0c;不要使用…

Java基础知识(四) -- 面向对象(下)

1.类变量和类方法 1.1 类变量背景 有一群小孩在玩堆雪人,不时有新的小孩加入,请问如何知道现在共有多少人在玩? 思路分析: 核心在于如何让变量count被所有对象共享 public class Child {private String name;// 定义静态变量(所有Child对象共享)public static int count 0;p…

Linux系统之stat命令的基本使用

Linux系统之stat命令的基本使用 一、stat命令 介绍二、stat命令帮助2.1 查询帮助信息2.2 stat命令的帮助解释 三、stat命令的基本使用3.1 查询文件信息3.2 查看文件系统状态3.3 使用格式化输出3.4 以简洁形式打印信息 四、注意事项 一、stat命令 介绍 stat 命令用于显示文件或文…

雷池 WAF 搭配阿里云 CDN 使用教程

雷池 WAF&#xff08;Web Application Firewall&#xff09;是一款强大的网络安全防护产品&#xff0c;通过实时流量分析和精准规则拦截&#xff0c;有效抵御各种网络攻击。在部署雷池 WAF 的同时&#xff0c;结合阿里云 CDN&#xff08;内容分发网络&#xff09;可以显著提升网…

蓝桥杯速成教程{三}(adc,i2c,uart)

目录 一、adc 原理图​编辑引脚配置 Adc通道使能配置 实例测试 ​编辑效果显示 案例程序 badc 按键相关函数 测量频率占空比 main 按键的过程 显示界面的过程 二、IIC通信-eeprom 原理图AT24C02 引脚配置 不可用状态&#xff0c;用的软件IIC 官方库移植 At24c02手册 ​编辑…

Semantic Segmentation Editor标注工具

https://github.com/Hitachi-Automotive-And-Industry-Lab/semantic-segmentation-editor https://docs.meteor.com/about/install.html https://v2-docs.meteor.com/install.html 安装指定版本的meteor curl https://install.meteor.com/\?release\2.12 | sh ubuntu18 安…

攻防世界web新手第四题easyphp

<?php highlight_file(__FILE__); $key1 0; $key2 0;$a $_GET[a]; $b $_GET[b];if(isset($a) && intval($a) > 6000000 && strlen($a) < 3){if(isset($b) && 8b184b substr(md5($b),-6,6)){$key1 1;}else{die("Emmm...再想想&quo…