游戏引擎学习第五天

这节貌似没讲什么
视频参考:https://www.bilibili.com/video/BV1Gmm2Y5EwE/

uint8 *A = somewhere in memory;
uint8 *B = somewhere in memory;

//BEFORE WE GOT TO HERE
int Y = *B; // == whatever was actually there before the 5
*A = 5;
int X = *B; // == 5
//Obviously! Y and X 
//DUH? The compiler should just load it once

在你的代码示例中:

以下是对每部分的解释:

  • uint8 *Auint8 *B:这两个是指向内存位置的指针,指向某个内存区域。具体的内存地址在代码中没有明确给出,因此我们不清楚它们指向的具体内存。

  • int Y = *B;:这行代码将指针 B 指向的内存位置的值解引用(即读取 *B)并将其赋值给 Y

  • *A = 5;:这行代码将指针 A 指向的内存位置的值设置为 5

  • int X = *B;:最后,代码再次解引用指针 B,将其值赋给 X

问题:为什么编译器不应该重新加载 *B,而是直接使用 Y 的值?

  • “编译器应该只加载一次”: 这段注释表示,从理论上讲,一旦 *B 的值被加载到 Y 中,之后不应该再次加载,因为在代码中并没有直接修改 B 指向的内存。因此,编译器本可以通过将 *B 的值存储到临时变量中,从而避免在 X 的赋值中再次解引用 B

为什么这种优化可能没有发生?

  1. 内存访问优化: 编译器在优化内存访问时,通常假设内存可能被程序的其他部分修改,尤其是如果这些内存可能是共享的。比如,AB 如果指向同一内存区域,或者内存的值可能被其他线程或硬件修改,编译器就不能假设 *B 在两次访问之间没有发生变化。

  2. 指针别名(Pointer Aliasing): 如果 AB 是指向同一块内存区域的指针,编译器不能假设 *B 的值在两次解引用之间不变,因为对 *A 的赋值可能会影响到 *B(例如,AB 指向同一内存)。因此,编译器不能简单地优化掉第二次对 *B 的解引用。

  3. 优化标志: 现代编译器有不同的优化等级,某些优化(如这类优化)只有在设置了较高的优化标志时才会执行(例如,GCC 中的 -O2-O3)。如果没有进行优化,或者优化级别较低(如 -O0),编译器可能不会执行这种优化。

  4. 内存的易变性(Volatility): 如果 AB 指向的内存被标记为 volatile,编译器就会强制每次都解引用它们,而不进行任何假设或优化。volatile 常用于与内存映射的 I/O、硬件寄存器或并发环境中的共享内存交互。

总结

简单来说,编译器 可以 通过将 *B 的值存储到一个临时变量中,避免第二次解引用 *B(即优化掉这次内存加载),前提是 编译器能够保证 *B 的值在两次解引用之间没有改变。然而,由于指针别名、并发操作或其他潜在的副作用,编译器可能不能进行这种优化。编译器是否进行优化取决于它对内存模型的理解以及它是否能保证两次解引用之间 *B 的值不变。

在这段代码中,MSG Message; 的声明位置取决于你对 MSG 变量的作用范围的需求。一般来说,建议将其声明放在 while 循环内部,而不是外部,原因如下:

  1. 局部性和性能:
    如果将 MSG 声明放在循环外部,MSG 变量将在每次循环时保持其值,可能导致不必要的残留数据。如果将其放在循环内部,每次循环都会创建一个新的 MSG 变量,避免了不必要的状态残留。

  2. 清晰的作用范围:
    MSG Message; 放在循环内部可以限制其作用范围,使得它只在当前的循环中有效。这使得代码更加清晰,并且减少了作用域问题。例如,如果你在多个地方使用 Message,会更容易理解它的生命周期和作用域。

  3. 避免潜在的逻辑错误:
    如果 MSG Message; 放在循环外部,它可能会在消息队列没有更新时继续保留上一个循环的消息,这可能会导致错误的行为。将其放在内部能确保每次迭代都能正确接收和处理新的消息。

因此,将 MSG Message; 放在 while 循环内部是更好的选择。你的代码可以这样优化:

if (Window) { // 检查窗口句柄是否有效,若有效则进入消息循环
  int xOffset = 0;
  int yOffset = 0;
  Running = true;
  while (Running) { // 启动一个无限循环,等待和处理消息
    MSG Message;    // 将 MSG 声明移至 while 循环内部
    while (PeekMessage(
        &Message,
        0, // hWnd 为 NULL,则检查当前线程中所有窗口的消息
        0, 0, // 用于设定消息类型的范围
        PM_REMOVE // 将消息从消息队列中移除,类似于 GetMessage 的行为
        )) {
      if (Message.message == WM_QUIT) {
        Running = false;
      }
      TranslateMessage(&Message); // 翻译消息,如果是键盘消息需要翻译
      DispatchMessage(&Message); // 分派消息,调用窗口过程处理消息
    }

    RenderWeirdGradient(xOffset, yOffset); // 渲染

    {
      HDC DeviceContext = GetDC(Window);
      RECT WindowRect;
      GetClientRect(Window, &WindowRect);
      int WindowWidth = WindowRect.right - WindowRect.left;
      int WindowHeight = WindowRect.bottom - WindowRect.top;
      Win32UpdateWindow(DeviceContext, &WindowRect, 0, 0, WindowWidth, WindowHeight);
      ReleaseDC(Window, DeviceContext);
    }

    ++xOffset;
  }
}

这样可以确保每次循环都从消息队列中正确获取新的消息,并且 MSG 变量的作用范围是局部的。

根据您提供的栈中变量地址和内容,可以绘制出堆栈结构图,展示各个局部变量如何在栈帧中存储。以下是一个简化的堆栈结构图:

栈结构图

+----------------------------------------------------+  <- 栈顶
|                    返回地址                        |  
+----------------------------------------------------+  
|                参数 cmdshow (int *)                |  <- 参数 cmdshow 存储在此,指向栈上的一个 int 值
|                0x000000721a8ffbc8                   |  
+----------------------------------------------------+  
|                参数 cmdline (char **)              |  <- 参数 cmdline 存储在此,指向栈上的一个字符串
|                0x000000721a8ffbc0                   |  
+----------------------------------------------------+  
|           参数 hInstPrev (HINSTANCE__ **)          |  <- 参数 hInstPrev 存储在此,指向一个空指针
|                0x000000721a8ffbb8                   |  
+----------------------------------------------------+  
|           参数 hInst (HINSTANCE__ **)               |  <- 参数 hInst 存储在此,指向一个有效的 HINSTANCE
|                0x000000721a8ffbb0                   |  
+----------------------------------------------------+  
|              WindowClass (WNDCLASS)                |  <- WindowClass 存储在此
|                0x000000721a8ff2f0                   |  
|                style = 0                           |  
|                lpfnWndProc = 0x0000000000000000     |  
|                cbClsExtra = 0                      |  
|                ...                                 |  
+----------------------------------------------------+  
|           BigOldBlockOfMemory (1024KB)             |  <- 大的内存块,占用 1024KB
|                0x000000721a8042d0                   |  
|                [1024KB 内存空间]                   |  <- 1MB 数据
+----------------------------------------------------+  <- 栈底

在这里插入图片描述

解释:

  1. 栈顶(返回地址):函数 WinMain 返回时,程序会跳到此地址。
  2. 参数 cmdshow, cmdline, hInstPrev, hInst:这些参数的值存储在栈帧中,按顺序排列:
    • cmdshow 存储了一个 int * 类型的地址,指向栈上的值 10
    • cmdline 存储了一个 char ** 类型的地址,指向栈上的字符串(为空字符串)。
    • hInstPrev 存储了一个 HINSTANCE__ ** 类型的地址,指向 NULL
    • hInst 存储了一个 HINSTANCE__ ** 类型的地址,指向有效的程序映像基址 game.exe
  3. WindowClass:是一个 WNDCLASS 结构体,包含多种成员(如 style, lpfnWndProc 等)。每个成员被初始化为零或 nullptr
  4. BigOldBlockOfMemory:这是一个 1MB 大小的 uint8 数组,栈上分配。它存储在 BigOldBlockOfMemory 地址处,内部包含 1024KB 的数据。

注意:

  • 栈分配顺序:栈的分配是从高地址向低地址方向进行的,所以栈顶的变量(返回地址)先存储,然后是函数参数,最后是局部变量(如 WindowClassBigOldBlockOfMemory)。
  • 指针的存储:对于指针类型的变量(如 cmdshow, cmdline, hInstPrev, hInst),它们存储的是指向实际数据的地址。这些数据的内容在栈帧中的其他位置。
// game.cpp : Defines the entry point for the application.
//

#include <cstdint>
#include <stdint.h>
#include <windows.h>

#define internal static        // 用于定义内翻译单元内部函数
#define local_persist static   // 局部静态变量
#define global_variable static // 全局变量

typedef uint8_t uint8;
typedef uint16_t uint16;
typedef uint32_t uint32;
typedef uint64_t uint64;

typedef int8_t int8;
typedef int16_t int16;
typedef int32_t int32;
typedef int64_t int64;

struct win32_offscreen_buffer {
  BITMAPINFO Info;
  void *Memory;
  // 后备缓冲区的宽度和高度
  int Width;
  int Height;
  int Pitch;
  int BytesPerPixel;
};

// TODO: 全局变量
global_variable bool Running;
global_variable win32_offscreen_buffer GlobalBackbuffer;

// 添加这个去掉重复的冗余代码
struct win32_window_dimension {
  int Width;
  int Height;
};

win32_window_dimension Win32GetWindowDimension(HWND Window) {
  win32_window_dimension Result;
  RECT ClientRect;
  GetClientRect(Window, &ClientRect);
  // 计算绘制区域的宽度和高度
  Result.Height = ClientRect.bottom - ClientRect.top;
  Result.Width = ClientRect.right - ClientRect.left;
  return Result;
}

// 渲染一个奇异的渐变图案
internal void RenderWeirdGradient(win32_offscreen_buffer Buffer, int BlueOffset,
                                  int GreenOffset) {
  // TODO:让我们看看优化器是怎么做的
  uint8 *Row = (uint8 *)Buffer.Memory;      // 指向位图数据的起始位置
  for (int Y = 0; Y < Buffer.Height; ++Y) { // 遍历每一行
    uint32 *Pixel = (uint32 *)Row;          // 指向每一行的起始像素
    for (int X = 0; X < Buffer.Width; ++X) { // 遍历每一列
      uint8 Blue = (X + BlueOffset);         // 计算蓝色分量
      uint8 Green = (Y + GreenOffset);       // 计算绿色分量
      *Pixel++ = ((Green << 8) | Blue);      // 设置当前像素的颜色
    }
    Row += Buffer.Pitch; // 移动到下一行
  }
}

// 这个函数用于重新调整 DIB(设备独立位图)大小
internal void Win32ResizeDIBSection(win32_offscreen_buffer *Buffer, int width,
                                    int height) {
  // device independent bitmap(设备独立位图)
  // TODO: 进一步优化代码的健壮性
  // 可能的改进:先不释放,先尝试其他方法,再如果失败再释放。
  if (Buffer->Memory) {
    VirtualFree(
        Buffer->Memory, // 指定要释放的内存块起始地址
        0, // 要释放的大小(字节),对部分释放有效,整体释放则设为 0
        MEM_RELEASE); // MEM_RELEASE:释放整个内存块,将内存和地址空间都归还给操作系统
  }
  // 赋值后备缓冲的宽度和高度
  Buffer->Width = width;
  Buffer->Height = height;
  Buffer->BytesPerPixel = 4;

  // 设置位图信息头(BITMAPINFOHEADER)
  Buffer->Info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); // 位图头大小
  Buffer->Info.bmiHeader.biWidth = Buffer->Width; // 设置位图的宽度
  Buffer->Info.bmiHeader.biHeight =
      -Buffer->Height; // 设置位图的高度(负号表示自上而下的方向)
  Buffer->Info.bmiHeader.biPlanes = 1; // 设置颜色平面数,通常为 1
  Buffer->Info.bmiHeader.biBitCount =
      32; // 每像素的位数,这里为 32 位(即 RGBA)
  Buffer->Info.bmiHeader.biCompression =
      BI_RGB; // 无压缩,直接使用 RGB 颜色模式

  // 创建 DIBSection(设备独立位图)并返回句柄
  // TODO:我们可以自己分配?
  int BitmapMemorySize =
      (Buffer->Width * Buffer->Height) * Buffer->BytesPerPixel;
  Buffer->Memory = VirtualAlloc(
      0, // lpAddress:指定内存块的起始地址。
         // 通常设为 NULL,由系统自动选择一个合适的地址。
      BitmapMemorySize, // 要分配的内存大小,单位是字节。
      MEM_COMMIT, // 分配物理内存并映射到虚拟地址。已提交的内存可以被进程实际访问和操作。
      PAGE_READWRITE // 内存可读写
  );
  Buffer->Pitch = width * Buffer->BytesPerPixel; // 每一行的字节数
  // TODO:可能会把它清除成黑色
}

// 这个函数用于将 DIBSection 绘制到窗口设备上下文
internal void Win32DisplayBufferInWindow(HDC DeviceContext, int WindowWidth,
                                         int WindowHeight,
                                         win32_offscreen_buffer Buffer, int X,
                                         int Y, int Width, int Height) {
  // 使用 StretchDIBits 将 DIBSection 绘制到设备上下文中
  StretchDIBits(
      DeviceContext, // 目标设备上下文(窗口或屏幕的设备上下文)
      /*
      X, Y, Width, Height, // 目标区域的 x, y 坐标及宽高
      X, Y, Width, Height,
      */
      0, 0, WindowWidth, WindowHeight,   //
      0, 0, Buffer.Width, Buffer.Height, //
      // 源区域的 x, y 坐标及宽高(此处源区域与目标区域相同)
      Buffer.Memory,  // 位图内存指针,指向 DIBSection 数据
      &Buffer.Info,   // 位图信息,包含位图的大小、颜色等信息
      DIB_RGB_COLORS, // 颜色类型,使用 RGB 颜色
      SRCCOPY); // 使用 SRCCOPY 操作符进行拷贝(即源图像直接拷贝到目标区域)
}

LRESULT CALLBACK
Win32MainWindowCallback(HWND hwnd, // 窗口句柄,表示消息来源的窗口
                        UINT Message, // 消息标识符,表示当前接收到的消息类型
                        WPARAM wParam, // 与消息相关的附加信息,取决于消息类型
                        LPARAM LParam) { // 与消息相关的附加信息,取决于消息类型
  LRESULT Result = 0; // 定义一个变量来存储消息处理的结果

  switch (Message) { // 根据消息类型进行不同的处理
  case WM_CREATE: {
    OutputDebugStringA("WM_CREATE\n");
  };
  case WM_SIZE: { // 窗口大小发生变化时的消息
  } break;

  case WM_DESTROY: { // 窗口销毁时的消息
    // TODO: 处理错误,用重建窗口
    Running = false;
  } break;

  case WM_CLOSE: { // 窗口关闭时的消息
    // TODO: 像用户发送消息进行处理
    Running = false;
  } break;

  case WM_ACTIVATEAPP: { // 应用程序激活或失去焦点时的消息
    OutputDebugStringA(
        "WM_ACTIVATEAPP\n"); // 输出调试信息,表示应用程序激活或失去焦点
  } break;

  case WM_PAINT: { // 处理 WM_PAINT 消息,通常在窗口需要重新绘制时触发
    PAINTSTRUCT Paint; // 定义一个 PAINTSTRUCT 结构体,保存绘制的信息
    // 调用 BeginPaint 开始绘制,并获取设备上下文 (HDC),同时填充 Paint 结构体
    HDC DeviceContext = BeginPaint(hwnd, &Paint);
    // 获取当前绘制区域的左上角坐标
    int X = Paint.rcPaint.left;
    int Y = Paint.rcPaint.top;

    // 计算绘制区域的宽度和高度
    int Height = Paint.rcPaint.bottom - Paint.rcPaint.top;
    int Width = Paint.rcPaint.right - Paint.rcPaint.left;

    win32_window_dimension Dimension = Win32GetWindowDimension(hwnd);

    Win32DisplayBufferInWindow(DeviceContext, Dimension.Width, Dimension.Height,
                               GlobalBackbuffer, X, Y, Width, Height);

#if 0
    local_persist DWORD Operation = WHITENESS;

    // 使用 WHITENESS 操作符填充矩形区域为白色
    PatBlt(DeviceContext, X, Y, Width, Height, Operation);

    // 设置窗体的颜色在刷新时白色和黑色之间来回变换
    if (Operation == WHITENESS) {
      Operation = BLACKNESS;
    } else {
      Operation = WHITENESS;
    }
#endif

    // 调用 EndPaint 结束绘制,并释放设备上下文
    EndPaint(hwnd, &Paint);
  } break;

  default: { // 对于不处理的消息,调用默认的窗口过程
    Result = DefWindowProc(hwnd, Message, wParam,
                           LParam); // 调用默认窗口过程处理消息
  } break;
  }

  return Result; // 返回处理结果
}

int CALLBACK WinMain(HINSTANCE hInst, HINSTANCE hInstPrev, //
                     PSTR cmdline, int cmdshow) {
  uint8 BigOldBlockOfMemory[1004 * 1024];
  WNDCLASS WindowClass = {};
  // 使用大括号初始化,所有成员都被初始化为零(0)或 nullptr

  Win32ResizeDIBSection(&GlobalBackbuffer, 1280, 720);

  // WindowClass.style:表示窗口类的样式。通常设置为一些 Windows
  // 窗口样式标志(例如 CS_HREDRAW, CS_VREDRAW)。
  WindowClass.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
  // CS_HREDRAW 当窗口的宽度发生变化时,窗口会被重绘。
  // CS_VREDRAW 当窗口的高度发生变化时,窗口会被重绘

  //  WindowClass.lpfnWndProc:指向窗口过程函数的指针,窗口过程用于处理与窗口相关的消息。
  WindowClass.lpfnWndProc = Win32MainWindowCallback;

  // WindowClass.hInstance:指定当前应用程序的实例句柄,Windows
  // 应用程序必须有一个实例句柄。
  WindowClass.hInstance = hInst;

  // WindowClass.lpszClassName:指定窗口类的名称,通常用于创建窗口时注册该类。
  WindowClass.lpszClassName = "gameWindowClass"; // 类名
  if (RegisterClass(&WindowClass)) {             // 如果窗口类注册成功
    HWND Window = CreateWindowEx(
        0,                         // 创建窗口,使用扩展窗口风格
        WindowClass.lpszClassName, // 窗口类的名称,指向已注册的窗口类
        "game",                    // 窗口标题(窗口的名称)
        WS_OVERLAPPEDWINDOW |
            WS_VISIBLE, // 窗口样式:重叠窗口(带有菜单、边框等)并且可见
        CW_USEDEFAULT, // 窗口的初始位置:使用默认位置(X坐标)
        CW_USEDEFAULT, // 窗口的初始位置:使用默认位置(Y坐标)
        CW_USEDEFAULT, // 窗口的初始宽度:使用默认宽度
        CW_USEDEFAULT, // 窗口的初始高度:使用默认高度
        0,             // 父窗口句柄(此处无父窗口,传0)
        0,             // 菜单句柄(此处没有菜单,传0)
        hInst,         // 当前应用程序的实例句柄
        0 // 额外的创建参数(此处没有传递额外参数)
    );
    // 如果窗口创建成功,Window 将保存窗口的句柄
    if (Window) { // 检查窗口句柄是否有效,若有效则进入消息循环
      int xOffset = 0;
      int yOffset = 0;
      Running = true;
      while (Running) { // 启动一个无限循环,等待和处理消息
        MSG Message;    // 声明一个 MSG 结构体,用于接收消息
        while (PeekMessage(
            &Message,
            // 指向一个 `MSG` 结构的指针。`PeekMessage`
            // 将在 `lpMsg` 中填入符合条件的消息内容。
            0,
            // `hWnd` 为`NULL`,则检查当前线程中所有窗口的消息;
            // 如果设置为特定的窗口句柄,则只检查该窗口的消息。
            0, //
            0, // 用于设定消息类型的范围
            PM_REMOVE // 将消息从消息队列中移除,类似于 `GetMessage` 的行为。
            )) {
          if (Message.message == WM_QUIT) {
            Running = false;
          }
          TranslateMessage(&Message); // 翻译消息,如果是键盘消息需要翻译
          DispatchMessage(&Message); // 分派消息,调用窗口过程处理消息
        }
        RenderWeirdGradient(GlobalBackbuffer, xOffset, yOffset);
        // 这个地方需要渲染一下不然是黑屏
        {
          HDC DeviceContext = GetDC(Window);

          win32_window_dimension Dimension = Win32GetWindowDimension(Window);

          RECT WindowRect;
          GetClientRect(Window, &WindowRect);
          int WindowWidth = WindowRect.right - WindowRect.left;
          int WindowHeigh = WindowRect.bottom - WindowRect.top;
          Win32DisplayBufferInWindow(DeviceContext, Dimension.Width,
                                     Dimension.Height, GlobalBackbuffer, 0, 0,
                                     WindowWidth, WindowHeigh);

          ReleaseDC(Window, DeviceContext);
        }
        ++xOffset;
      }
    } else { // 如果窗口创建失败
             // 这里可以处理窗口创建失败的逻辑
             // 比如输出错误信息,或退出程序等
             // TODO:
    }
  } else { // 如果窗口类注册失败
           // 这里可以处理注册失败的逻辑
           // 比如输出错误信息,或退出程序等
           // TODO:
  }

  return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

大路灯护眼灯十大品牌哪个牌子好?儿童大路灯护眼灯品牌排行榜

大路灯护眼灯十大品牌哪个牌子好&#xff1f;长时间在不良光线下用眼很容易引起视觉疲劳&#xff0c;最终影响视力健康&#xff0c;这个时候大路灯护眼灯以良好的性能成为了很不错的照明产品。不过如今行业热度很高&#xff0c;网红跨界品牌大路灯护眼灯出于成本压缩&#xff0…

1.2 图像处理基本操作

在本实战中&#xff0c;我们将学习如何使用OpenCV进行基本的图像处理操作。首先&#xff0c;我们将通过cv2.imread()函数读取图像&#xff0c;并使用cv2.imshow()在窗口中显示它。接着&#xff0c;我们将探索如何通过cv2.imwrite()保存图像&#xff0c;并设置不同的参数以控制图…

K8S如何基于Istio实现全链路HTTPS

K8S如何基于Istio实现全链路HTTPS Istio 简介Istio 是什么?为什么选择 Istio?Istio 的核心概念Service Mesh(服务网格)Data Plane(数据平面)Sidecar Mode(边车模式)Ambient Mode(环境模式)Control Plane(控制平面)Istio 的架构与组件Envoy ProxyIstiod其他组件Istio 的流量管…

手动搭建 Ghost 博客

操作场景 Ghost 是使用 Node.js 语言编写的开源博客平台&#xff0c;您可使用 Ghost 快速搭建博客&#xff0c;简化在线出版过程。本文档介绍如何在腾讯云云服务器&#xff08;CVM&#xff09;上手动搭建 Ghost 个人网站。 进行 Ghost 网站搭建&#xff0c;您需要熟悉 Linux …

MySQL之索引(3)(索引基本语法、SQL执行计划、常见索引失效原因与解决方法)

目录 一、索引基本语法。 &#xff08;1&#xff09;创建索引。 &#xff08;2&#xff09;查看索引。 &#xff08;3&#xff09;删除索引。 &#xff08;4&#xff09;给多列添加组合索引。 1、何时添加索引&#xff1f;&#xff1f; 2、组合索引。 二、SQL执行计划。 &#…

前端中的 File 和 Blob两个对象到底有什么不同

JavaScript 在处理文件、二进制数据和数据转换时&#xff0c;提供了一系列的 API 和对象&#xff0c;比如 File、Blob、FileReader、ArrayBuffer、Base64、Object URL 和 DataURL。每个概念在不同场景中都有重要作用。下面的内容我们将会详细学习每个概念及其在实际应用中的用法…

一步一步从asp.net core mvc中访问asp.net core WebApi

"从asp.net core mvc中访问asp.net core WebApi"看到这个标题是不是觉得很绕口啊&#xff0c;但的确就是要讲一讲这样的访问。前面我们介绍了微信小程序访问asp.net core webapi(感兴趣的童鞋可以看看前面的博文有关WEBAPI的搭建)&#xff0c;这里我们重点不关心如何…

【Linux系列】VNC安装ssh后,ssh无法登录

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

温度虽寒,其道犹变:OpenAI接口之温度参数设置为0,为何每次回复仍有不确定性?

问题描述 调用openai API&#xff0c;使用templature 0&#xff0c;每次返回的内容仍有一些不同 >>> client OpenAI( ... api_keyapi_key, ... base_urlapi_base) #第一次尝试 >>> response client.chat.completions.create(mo…

【软件测试】需求的概念和常见模型(瀑布、螺旋、增量、迭代)

1. 什么是需求 在企业中&#xff0c;经常会听到&#xff1a;用户需求和软件需求 用户需求&#xff1a;没用经过合理的评估&#xff0c;通常就是一句话&#xff08;开发一个五彩斑斓的黑&#xff09;软件需求&#xff1a;开发人员和测试人员执行工作的依据 1.2 软件需求 在工…

食品配送管理系统(源码+文档+部署+讲解)

食品配送管理系统是成品商业化项目&#xff0c;系统可基于源码二开。 系统概述 餐饮食品配送&#xff0c;包含配送人APP、下单APP、管理端等&#xff0c;实现订餐、配餐&#xff0c;用于食品店、中央厨房等订餐、团餐业务 本项目名称为食品配送系统&#xff0c;是针对食品配…

./bin/mindieservice_daemon启动成功

接MindIE大模型测试及报错Fatal Python error: PyThreadState_Get: the function must be called with the GIL held,-CSDN博客经过调整如下红色部分参数&#xff0c;昇腾310P3跑起来了7b模型&#xff1a; rootdev-8242526b-01f2-4a54-b89d-f6d9c57c692d-qjhpf:/home/apulis-de…

我谈维纳(Wiener)复原滤波器

Rafael Gonzalez的《数字图像处理》中&#xff0c;图像复原这章内容几乎全错。上篇谈了图像去噪&#xff0c;这篇谈图像复原。 图像复原也称为盲解卷积&#xff0c;不处理点扩散函数&#xff08;光学传递函数&#xff09;的都不是图像复原。几何校正不属于图像复原&#xff0c…

精选 Top10 开源调度工具,解锁高效工作负裁自动化

在大数据和现代 IT 环境中&#xff0c;任务调度与工作负载自动化&#xff08;WLA&#xff09;工具是优化资源利用、提升生产效率的核心驱动力。随着企业对数据分析、实时处理和多地域任务调度需求的增加&#xff0c;这些工具成为关键技术。 本文将介绍当前技术发展背景下的Top …

高效视觉方案:AR1335与i.MX8MP的完美结合

方案采用NXP i.MX8MP处理器和onsemi AR1335图像传感器&#xff0c;i.MX8MP集成四核Cortex-A53、NPU及双ISP技术。AR1335是一颗分辨率为13M的CMOS传感器。它使用了先进的BSI技术&#xff0c;提供了超高的分辨率和出色的低光性能&#xff0c;非常适合于需要高质量图像的应用。此外…

Ubuntu+ROS 机械臂拾取和放置

官方链接&#xff1a;https://github.com/skumra/baxter-pnp 1.下载并安装 SDK 依赖项 sudo apt-get install python-wstool python-rosdep 2.创建新的 catkin 工作区 mkdir -p ~/ros_ws/src cd ~/ros_ws/src 3.使用 wstool 下载 rosinstall 文件并将其复制到 Catkin 工作区…

论文阅读《Structure-from-Motion Revisited》

摘要 增量式地运动结构恢复是从无序图像集合中进行三维重建的一个普遍策略。虽然增量式地重建系统在各个方面上都取得了巨大的进步&#xff0c;但鲁棒性、准确性、完整度和尺度仍然是构建真正通用管道的关键问题。我们提出了一种新的运动结构恢复技术&#xff0c;它改进了目前…

基于Spring Boot的船运物流管理系统的设计与实现,LW+源码+讲解

摘要 近年来&#xff0c;信息化管理行业的不断兴起&#xff0c;使得人们的日常生活越来越离不开计算机和互联网技术。首先&#xff0c;根据收集到的用户需求分析&#xff0c;对设计系统有一个初步的认识与了解&#xff0c;确定船运物流管理系统的总体功能模块。然后&#xff0…

威联通Docker Compose搭建NAS媒体库资源工具NAS Tools

文章目录 一、环境配置1-1 需要的配件1-2 环境安装及配置注意:获取PUID/PGID1-3 目录位置准备总结,这里我们要做5件事备注:Docker无法下载解决办法二、登录配件,进行配件连接和配置2-1 jackett设置2-2 qBittorrent设置!!!设置文件下载地址2-3 jellyfin设置2-4 NASTools设…

Spring Boot - 扩展点 EnvironmentPostProcessor源码分析及真实案例

文章目录 概述EnvironmentPostProcessor 作用EnvironmentPostProcessor 实现和注册创建类并实现接口注册到 Spring Boot常见应用场景 源码分析1. EnvironmentPostProcessor 接口定义2. 扩展点加载流程3. 加载 EnvironmentPostProcessor 实现类4. EnvironmentPostProcessor 执行…