Làm thế nào để nén kết cấu phần cứng hoạt động?


13

Rằng nó nén dữ liệu so với mảng pixel là hiển nhiên.

Nhưng điều gì làm cho nó khác với nén thông thường (như png, jpeg)?


"Nén bình thường" là gì - những thứ như JPEG và PNG? Bạn có hỏi về sự khác biệt giữa các định dạng và hỗ trợ phần cứng như DXT và ASTC không?
Nathan Reed

6
(Cuối cùng, một chủ đề tôi biết một chút về!) Điều làm cho nó khác với PNG / JPEG là truy cập ngẫu nhiên. Cho rằng bạn muốn truy cập Texel (XY), bạn có thể nhanh chóng xác định dấu chân nhỏ của dữ liệu cần thiết để tạo texel đó. JPG hoặc PNG có thể yêu cầu giải nén tối đa tất cả dữ liệu! Phần 1 và 2 của bài viết Wikipedia là một bản tóm tắt tốt.
Simon F

Như SimonF đã viết. Đây là một câu hỏi cực kỳ rộng và câu trả lời phụ thuộc vào loại bạn quan tâm. Bạn đã xem đặc tả ví dụ DXT chưa?
imallett

Câu trả lời:


24

Như nhận xét của Simon đã ám chỉ, một điểm khác biệt chính giữa nén kết cấu phần cứng và nén hình ảnh thường được sử dụng khác là trước đây không sử dụng mã hóa entropy. Mã hóa Entropy là việc sử dụng các chuỗi bit ngắn hơn để biểu diễn các mẫu thường xảy ra hoặc lặp lại trong dữ liệu nguồn, như đã thấy trong các định dạng chứa như ZIP, nhiều định dạng hình ảnh phổ biến như GIF, JPEG và PNG và trong nhiều âm thanh phổ biến và định dạng video.

Mã hóa Entropy rất tốt trong việc nén tất cả các loại dữ liệu, nhưng về bản chất nó tạo ra một tỷ lệ nén thay đổi. Một số khu vực của hình ảnh có thể có ít chi tiết (hoặc chi tiết được dự đoán tốt bởi mô hình mã hóa bạn đang sử dụng) và yêu cầu rất ít bit, nhưng các khu vực khác có thể có chi tiết phức tạp đòi hỏi nhiều bit hơn để mã hóa. Điều này gây khó khăn cho việc triển khai truy cập ngẫu nhiên, vì không có cách nào đơn giản để tính toán vị trí trong dữ liệu nén mà bạn có thể tìm thấy pixel ở mức đã cho ( xy) tọa độ. Ngoài ra, hầu hết các lược đồ mã hóa entropy đều có trạng thái, do đó không thể đơn giản bắt đầu giải mã tại một vị trí tùy ý trong luồng; bạn phải bắt đầu lại từ đầu để xây dựng trạng thái chính xác. Tuy nhiên, truy cập ngẫu nhiên là cần thiết để lấy mẫu kết cấu, vì một shader có thể lấy mẫu từ bất kỳ vị trí nào trong kết cấu bất cứ lúc nào.

Vì vậy, thay vì mã hóa entropy, nén phần cứng sử dụng các sơ đồ dựa trên khối, tỷ lệ cố định. Ví dụ, trong nén DXT / BCn , kết cấu được cắt thành các khối pixel 4 × 4, mỗi khối được mã hóa thành 64 hoặc 128 bit (tùy thuộc vào định dạng nào được chọn); trong ASTC , các định dạng khác nhau sử dụng kích thước khối từ 4 × 4 đến 12 × 12 và tất cả các khối được mã hóa thành 128 bit. Chi tiết về cách các bit biểu thị dữ liệu hình ảnh khác nhau giữa các định dạng (và thậm chí có thể thay đổi từ khối này sang khối khác trong cùng một hình ảnh), nhưng vì tỷ lệ này được cố định, nên phần cứng dễ dàng tính toán vị trí trong bộ nhớ để tìm khối chứa một pixel ( x , y ) nhất định và mỗi khối được khép kín, do đó, nó có thể được giải mã độc lập với bất kỳ khối nào khác.

Một xem xét khác trong nén kết cấu phần cứng là việc giải mã nên được thực hiện một cách hiệu quả trong phần cứng. Điều này có nghĩa là các phép toán nặng và dataflow phức tạp bị biến dạng mạnh. Chẳng hạn, các định dạng BCn có thể được giải mã bằng cách thực hiện một số ít các phép toán số nguyên 8 bit cho mỗi khối để điền vào bảng tra cứu nhỏ, sau đó chỉ cần tra cứu mục nhập bảng thích hợp trên mỗi pixel. Điều này đòi hỏi rất ít diện tích trên chip, điều này rất quan trọng vì có lẽ bạn muốn giải mã một số khối song song và do đó cần một vài bản sao của phần cứng giải mã.

Ngược lại, các định dạng dựa trên DCT như JPEG yêu cầu số lượng toán học không cần thiết cho mỗi pixel, chưa kể đến một luồng dữ liệu phức tạp hoán đổi và phát các giá trị trung gian khác nhau qua các pixel trong một khối. (Hãy xem bài viết này để biết một số chi tiết chính xác về giải mã DCT.) Đây sẽ là một công cụ rất nhiều cho việc triển khai phần cứng, mà tôi đoán là tại sao AFAICT, không có phần cứng GPU nào thực hiện nén kết cấu dựa trên DCT hoặc sóng con .


câu trả lời tuyệt vời. Ngoài ra, bạn có thể thêm rằng có một số bài báo đề cập rằng trong một số trường hợp, bạn có thể tự nén và giải mã nó bằng mã máy khách trong trình đổ bóng pixel, thay vì phụ thuộc vào phần cứng chuyên dụng. Tôi biết rằng không có thế giới thực sử dụng điều đó, điều đó có thể chỉ có giá trị cho nghiên cứu, nhưng nó tồn tại.
v.oddou

1
Thực tế, @ Nathan-Reed nén lại dựa trên biến đổi, thực tế, dự án Talisman của microsofts đã sử dụng sơ đồ nén có tên TREC (một trong các chế độ) đã sử dụng DCT nhưng không giống như JPEG, cho phép truy cập ngẫu nhiên vào các khối (tôi nghi ngờ phải có một bảng chứa địa chỉ). Điều này sau đó cho phép dữ liệu có độ dài thay đổi cho các khối khác nhau, nhưng sự khó chịu đối với CTNH - một lý do khiến VQ TC không còn hợp thời. FWIW Tôi đã thử nghiệm với khoảng một chục ý tưởng TC B4 PVRTC; một số là các coeffs có tỷ lệ cố định, dựa trên biến đổi, nhưng "thiếu" vẫn sử dụng các bit. Các địa điểm coeff cố định giống như BTC ngụ ý thông tin "miễn phí".
Simon F

2
@ Nathan-Sậy. Từ những gì tôi đã thấy, tất cả các bộ giải mã CTNH có thể được thực hiện với đường dẫn logic thuần túy (giải mã bit, một số tra cứu, một số toán học trong đường dẫn dữ liệu) nhưng không cần thiết phải lặp / đăng ký. Bạn có biết bất kỳ lược đồ nào thêm độ trễ chu kỳ vào tra cứu kết cấu không? (Tôi rất vui khi triển khai bộ giải mã VHDL ETC1) Tôi có ấn tượng rằng mỗi đơn vị kết cấu (TU) có bộ giải mã được nhúng.
Romain Piquois

30

"Làm thế nào (phần cứng) kết cấu nén hoạt động" là một chủ đề lớn. Hy vọng rằng tôi có thể cung cấp một số hiểu biết mà không cần sao chép nội dung câu trả lời của Nathan .

Yêu cầu

Nén họa tiết thường khác với các kỹ thuật nén hình ảnh 'tiêu chuẩn', ví dụ JPEG / PNG theo bốn cách chính, như được nêu trong Kết xuất của Beers et al từ Kết cấu nén :

  1. Tốc độ giải mã : Bạn không muốn nén kết cấu chậm hơn (ít nhất là không đáng chú ý như vậy) so với sử dụng kết cấu không nén. Việc giải nén cũng tương đối đơn giản vì điều đó có thể giúp đạt được giải nén nhanh mà không tốn quá nhiều chi phí phần cứng và điện năng.

  2. Truy cập ngẫu nhiên : Bạn không thể dễ dàng dự đoán những texels nào sẽ được yêu cầu trong một kết xuất cụ thể. Nếu một số tập hợp con, M , của các texels được truy cập đến từ giữa hình ảnh, điều cần thiết là bạn không phải giải mã tất cả các dòng 'trước đó' của kết cấu để xác định M ; với JPEG và PNG, điều này là cần thiết vì giải mã pixel phụ thuộc vào dữ liệu được giải mã trước đó.
    Lưu ý, đã nói điều này, chỉ vì bạn có quyền truy cập "ngẫu nhiên", không có nghĩa là bạn nên thử lấy mẫu hoàn toàn tùy ý

  3. Tốc độ nén và chất lượng hình ảnh : Beers et al lập luận (một cách thuyết phục) rằng mất một số chất lượng trong kết quả nén để cải thiện tốc độ nén là một sự đánh đổi đáng giá. Trong kết xuất 3D, dữ liệu có thể sẽ bị thao túng (ví dụ: được lọc & tô bóng, v.v.) và do đó, một số mất chất lượng cũng có thể bị che giấu.

  4. Mã hóa / giải mã bất đối xứng : Mặc dù có thể gây tranh cãi hơn một chút, nhưng họ cho rằng có thể chấp nhận quá trình mã hóa chậm hơn nhiều so với giải mã. Cho rằng việc giải mã cần phải ở mức điền vào CTNH, điều này thường được chấp nhận. (Tôi sẽ thừa nhận rằng việc nén PVRTC, ETC2 và một số khác với chất lượng tối đa có thể nhanh hơn)

Lịch sử & Kỹ thuật sớm

Một số người có thể ngạc nhiên khi biết rằng nén kết cấu đã tồn tại hơn ba thập kỷ. Các trình mô phỏng bay từ thập niên 70 và 80 cần truy cập vào lượng dữ liệu kết cấu tương đối lớn và cho rằng 1 MB RAM vào năm 1980 là> $ 6000 , giảm dấu chân kết cấu là điều cần thiết. Một ví dụ khác, vào giữa những năm 70, ngay cả một lượng nhỏ bộ nhớ và logic tốc độ cao, ví dụ, đủ cho bộ đệm khung hình 512x512 RGB khiêm tốn ) có thể khiến bạn trả lại giá cho ngôi nhà nhỏ.

Mặc dù, AFAIK, không được gọi một cách rõ ràng là nén kết cấu, trong tài liệu và bằng sáng chế, bạn có thể tìm thấy các tài liệu tham khảo về các kỹ thuật bao gồm:
a. các hình thức đơn giản của tổng hợp kết cấu toán học / thủ tục,
b. sử dụng một kết cấu kênh đơn (ví dụ 4bpp) sau đó được nhân với giá trị RGB trên mỗi kết cấu,
c. YUV, và
d. bảng màu (tài liệu gợi ý sử dụng phương pháp của Heckbert để thực hiện nén)

Mô hình hóa dữ liệu hình ảnh

Như đã lưu ý ở trên, nén kết cấu gần như luôn luôn bị mất và do đó, vấn đề trở thành một trong những cố gắng thể hiện dữ liệu quan trọng theo cách gọn nhẹ trong khi xử lý thông tin ít quan trọng hơn. Các lược đồ khác nhau sẽ được mô tả dưới đây đều có mô hình 'tham số hóa' gần đúng với hành vi điển hình của dữ liệu kết cấu và phản ứng của mắt.

Hơn nữa, vì nén kết cấu có xu hướng sử dụng mã hóa tốc độ cố định, quá trình nén thường bao gồm một bước tìm kiếm để tìm tập hợp các tham số, khi được đưa vào mô hình, sẽ tạo ra một xấp xỉ tốt của kết cấu ban đầu. Bước tìm kiếm đó, tuy nhiên, có thể tốn thời gian.
(Với ngoại lệ có thể có của các công cụ như optipng , đây là một lĩnh vực khác mà việc sử dụng PNG & JPEG điển hình khác với các sơ đồ nén kết cấu)

Trước khi tiến xa hơn, để giúp hiểu thêm về TC, bạn nên xem qua Phân tích thành phần chính (PCA) - một công cụ toán học rất hữu ích để nén dữ liệu.

Kết cấu ví dụ

Để so sánh các phương pháp khác nhau, chúng tôi sẽ sử dụng hình ảnh sau:

lorikeet nhỏ + văn bản
Lưu ý rằng đây là một hình ảnh khá khó khăn, đặc biệt là đối với các phương thức bảng màu và VQTC vì nó trải dài trên nhiều khối màu RGB và chỉ 15% số texel sử dụng màu lặp đi lặp lại.

PC và (post mid 90s) Nén Texture Console

Để giảm chi phí dữ liệu, một số trò chơi trên PC và máy chơi game đầu tiên cũng sử dụng hình ảnh bảng màu, đây là một hình thức của Vector Quantisation (VQ). Các cách tiếp cận dựa trên bảng màu đưa ra giả định rằng một hình ảnh nhất định chỉ sử dụng các phần tương đối nhỏ của khối màu RGB (A). Một vấn đề với kết cấu bảng màu là tỷ lệ nén cho chất lượng đạt được nhìn chung khá khiêm tốn. Kết cấu ví dụ được nén thành "4bpp" (sử dụng GIMP) được tạo lại Lưu ý rằng đây là một hình ảnh tương đối khó khăn cho các sơ đồ VQ.
nhập mô tả hình ảnh ở đây

VQ với các vectơ lớn hơn (ví dụ ARb 2bpp)

Lấy cảm hứng từ Beers et al, bảng điều khiển Dreamcast đã sử dụng VQ để mã hóa các khối pixel 2x2 hoặc thậm chí 2x4 bằng các byte đơn. Trong khi "vectơ" trong kết cấu bảng màu là 3 hoặc 4 chiều, các khối pixel 2x2 có thể được coi là 16 chiều. Sơ đồ nén giả định có đủ, sự lặp lại gần đúng của các vectơ này.

Mặc dù VQ có thể đạt được chất lượng thỏa đáng với ~ 2bpp, nhưng vấn đề với các lược đồ này là nó yêu cầu đọc bộ nhớ phụ thuộc: Một lần đọc ban đầu từ bản đồ chỉ mục để xác định mã cho pixel được theo sau bởi một giây để thực sự lấy dữ liệu pixel liên quan với mã đó. Bộ nhớ cache bổ sung có thể giúp giảm bớt một số độ trễ phát sinh, nhưng làm tăng thêm độ phức tạp cho phần cứng.

Hình ảnh ví dụ được nén với sơ đồ Dreamcast 2bpp là Kết quả VQ 2bpp. Bản đồ chỉ số là:Bản đồ chỉ số VQ 2bpp

Việc nén dữ liệu VQ có thể được thực hiện theo nhiều cách khác nhau, tuy nhiên, IIRC , việc trên đã được thực hiện bằng PCA để lấy và sau đó phân vùng các vectơ 16D dọc theo vectơ chính thành 2 bộ sao cho hai vectơ đại diện giảm thiểu sai số bình phương trung bình. Quá trình sau đó được đệ quy cho đến khi 256 vectơ ứng cử viên được tạo ra. Cách tiếp cận thuật toán toàn cầu k-mean / Lloyd đã được áp dụng để cải thiện các đại diện.

Biến đổi không gian màu

Các phép biến đổi không gian màu cũng sử dụng PCA lưu ý rằng sự phân bố màu toàn cầu thường được trải dọc theo một trục chính với sự lan truyền ít hơn nhiều dọc theo các trục khác. Đối với các đại diện của YUV, các giả định là a) trục chính thường theo hướng luma và b) mắt nhạy cảm hơn với những thay đổi theo hướng này.

Hệ thống Voodoo 3dfx đã cung cấp "YAB" , hệ thống nén 8bpp, "Kênh hẹp" chia mỗi texel 8 bit thành định dạng 322 và áp dụng một người dùng đã chọn chuyển đổi màu cho dữ liệu đó để ánh xạ nó thành RGB. Do đó, trục chính có 8 cấp độ và các trục nhỏ hơn, mỗi cấp độ 4.

Chip S3 Virge có sơ đồ 4bpp đơn giản hơn một chút, cho phép người dùng chỉ định, cho toàn bộ kết cấu , hai màu kết thúc, nằm trên trục chính, cùng với kết cấu đơn sắc 4bpp. Giá trị mỗi pixel sau đó trộn các màu kết thúc với các trọng số phù hợp để tạo ra kết quả RGB.

Đề án dựa trên BTC

Tua lại một số năm, Delp và Mitchell đã thiết kế một sơ đồ nén hình ảnh đơn sắc (đơn sắc) được gọi là Mã hóa cắt khối , (BTC) . Bài viết này cũng bao gồm một thuật toán nén, nhưng, với mục đích của chúng tôi, chúng tôi chủ yếu quan tâm đến dữ liệu nén kết quả và quá trình giải nén.

Trong sơ đồ này, hình ảnh được chia thành các khối pixel 4 x 4, có thể được nén độc lập với thuật toán VQ cục bộ. Mỗi khối được biểu thị bằng hai "giá trị", ab và một tập hợp các bit chỉ mục 4 x 4, xác định giá trị nào trong hai giá trị được sử dụng cho mỗi pixel.

S3TC : 4bpp RGB (+ 1bit alpha)
Mặc dù một số màu biến thể của BTC cho nén hình ảnh được đề xuất, quan tâm đến chúng tôi là Iourcha et al của S3TC , một số trong đó dường như là một sự tái khám phá công việc hơi bị lãng quên của Hoffert et al mà đã được sử dụng trong Quicktime của Apple.

S3TC ban đầu, không có các biến thể DirectX, nén các khối của RGB hoặc RGB + 1bit Alpha thành 4bpp. Mỗi khối 4 x 4 trong kết cấu được thay thế bằng hai màu cuối là AB , từ đó có tới hai màu khác được lấy từ các hỗn hợp tuyến tính, trọng lượng cố định. Hơn nữa, mỗi texel trong khối có chỉ số 2 bit xác định cách chọn một trong bốn màu này.

Ví dụ, phần sau đây là phần 4 pixel của hình ảnh thử nghiệm được nén bằng công cụ Máy nén AMD / ATI. ( Về mặt kỹ thuật, nó được lấy từ phiên bản 512x512 của hình ảnh thử nghiệm nhưng tha thứ cho việc tôi không có thời gian để cập nhật các ví dụ ). Điều này minh họa quá trình nén: Tính trung bình và trục chính của các màu được tính toán. Sau đó, một sự phù hợp tốt nhất được thực hiện để tìm hai điểm kết thúc 'nằm trên' trục, cùng với hai pha trộn 1: 2 và 2: 1 xuất phát (hoặc trong một số trường hợp là pha trộn 50:50) của các điểm cuối đó, đó là giảm thiểu lỗi Mỗi pixel gốc sau đó được ánh xạ tới một trong những màu đó để tạo ra kết quả.
nhập mô tả hình ảnh ở đây

Nếu, như trong trường hợp này, màu sắc được xấp xỉ hợp lý bởi trục chính, sai số sẽ tương đối thấp. Tuy nhiên, nếu như trong khối 4x4 lân cận được hiển thị bên dưới, màu sắc đa dạng hơn, sai số sẽ cao hơn.
nhập mô tả hình ảnh ở đây

Hình ảnh ví dụ, được nén bằng Máy nén AMD tạo ra:
nhập mô tả hình ảnh ở đây

Vì các màu được xác định độc lập trên mỗi khối, có thể có sự không liên tục tại các ranh giới khối, nhưng miễn là độ phân giải được giữ đủ cao, các tạo tác khối này có thể không được chú ý:
nhập mô tả hình ảnh ở đây

ETC1 : 4bpp RGB
Ericsson Texture Nén cũng hoạt động với các khối 4 x 4 nhưng đưa ra giả định rằng, giống như YUV, trục chính của một bộ texels cục bộ thường có mối tương quan rất mạnh với "luma". Tập hợp các texels sau đó có thể được biểu diễn chỉ bằng một màu trung bình và một "chiều dài" vô hướng được lượng tử hóa cao của phép chiếu của các texels lên trục giả định đó.

Vì điều này giúp giảm chi phí lưu trữ dữ liệu so với S3TC, nó cho phép ETC giới thiệu sơ đồ phân vùng, theo đó, khối 4x4 được chia thành một cặp khối phụ 4x2 hoặc dọc 2x4. Những cái này có màu trung bình của riêng mình. Hình ảnh ví dụ tạo ra: Khu vực xung quanh mỏ cũng minh họa phân vùng ngang và dọc của các khối 4 x 4.
nhập mô tả hình ảnh ở đây
nhập mô tả hình ảnh ở đây

Toàn cầu + địa phương

Có một số hệ thống nén kết cấu là sự giao thoa giữa các sơ đồ toàn cầu và cục bộ, chẳng hạn như các bảng màu phân tán của Ivanov và Kuzmin hoặc phương pháp của PVRTC .

PVRTC : 4 & 2 bpp RGBA
PVRTC giả định rằng hình ảnh được nâng cấp (trong thực tế, song tuyến tính) là một xấp xỉ tốt với mục tiêu có độ phân giải đầy đủ và sự khác biệt giữa xấp xỉ và mục tiêu, tức là hình ảnh delta, là đơn sắc cục bộ, tức là có trục chính chiếm ưu thế. Hơn nữa, nó giả định trục chính cục bộ có thể được nội suy trên hình ảnh.

(phải làm: Thêm hình ảnh hiển thị sự cố)

Kết cấu ví dụ, được nén bằng PVRTC1 4bpp tạo ra: với khu vực xung quanh mỏ: So với sơ đồ BTC, các tạo tác khối thường bị loại bỏ nhưng đôi khi có thể bị " lấn lướt " nếu có sự gián đoạn mạnh trong hình ảnh nguồn, ví dụ như xung quanh hình bóng của đầu lorikeet.
nhập mô tả hình ảnh ở đây

nhập mô tả hình ảnh ở đây

Biến thể 2bpp, tự nhiên, có lỗi cao hơn so với 4bpp (lưu ý mất độ chính xác xung quanh vùng màu xanh lam, tần số cao gần cổ) nhưng vẫn có thể có chất lượng hợp lý:
nhập mô tả hình ảnh ở đây

Một lưu ý về chi phí giải nén

Mặc dù các thuật toán nén cho các sơ đồ được mô tả ở trên có chi phí đánh giá từ trung bình đến cao, các thuật toán giải nén, đặc biệt là cho việc triển khai phần cứng, tương đối rẻ tiền. ETC1, ví dụ, yêu cầu ít hơn một vài MUX và các bộ cộng có độ chính xác thấp; S3TC có hiệu quả hơn một số đơn vị bổ sung để thực hiện pha trộn; và PVRTC, một chút nữa. Về lý thuyết, các sơ đồ TC đơn giản này có thể cho phép kiến ​​trúc GPU tránh giải nén cho đến trước giai đoạn lọc, do đó tối đa hóa hiệu quả của bộ nhớ trong.

Đề án khác

Các chế độ TC phổ biến khác cần được đề cập là:

  • ETC2 - là siêu bộ (4bpp) của ETC1 giúp cải thiện việc xử lý các vùng có phân phối màu không phù hợp với 'luma'. Ngoài ra còn có một biến thể 4bpp hỗ trợ alpha 1 bit và định dạng 8bpp cho RGBA.

  • ATC - Thực sự là một biến thể nhỏ trên S3TC .

  • FXT1 (3dfx) là một biến thể tham vọng hơn của chủ đề S3TC .

  • BC6 & BC7: Một hệ thống dựa trên khối 8 bit, hỗ trợ ARGB. Ngoài các chế độ HDR, chúng sử dụng một hệ thống phân vùng phức tạp hơn so với ETC để cố gắng mô hình hóa phân phối màu hình ảnh tốt hơn.

  • PVRTC2: ARGB 2 & 4bpp. Điều này giới thiệu các chế độ bổ sung bao gồm một để khắc phục các hạn chế với các ranh giới mạnh trong ảnh.

  • ASTC: Đây cũng là một hệ thống dựa trên khối nhưng có phần phức tạp hơn ở chỗ nó có một số lượng lớn kích thước khối có thể nhắm mục tiêu vào một phạm vi rộng của bpp. Nó cũng bao gồm các tính năng như tối đa 4 vùng phân vùng với trình tạo phân vùng giả ngẫu nhiên và độ phân giải biến cho dữ liệu chỉ mục và / hoặc độ chính xác màu và mô hình màu.


1
Wow, đây nên là một bài viết blog ở đâu đó! Câu trả lời chính xác!
glampert

2
Vui mừng vì nó giúp đỡ. Đối với một blog, tôi đã viết điều này một thập kỷ trước nhưng tôi thực sự không có thời gian để làm chúng.
Simon F
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.